From d692e35e058d7f6439e8f7cf29580eef745df968 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 23 Jul 2025 14:21:24 -0700 Subject: [PATCH 01/81] wip --- .devcontainer/Dockerfile | 19 ++- .devcontainer/config/api/dotenv | 15 ++ .../config/appointment-frontend/dotenv.local | 3 + .../public/config/configuration.json | 15 ++ .../public/config/kc/keycloak-public.json | 8 + .../public/static/keycloak/keycloak.json | 8 + .devcontainer/devcontainer.json | 67 ++++---- .devcontainer/docker-compose.yml | 14 +- .devcontainer/postCreateCommand.sh | 150 ++++++++++-------- 9 files changed, 174 insertions(+), 125 deletions(-) create mode 100644 .devcontainer/config/appointment-frontend/dotenv.local create mode 100644 .devcontainer/config/appointment-frontend/public/config/configuration.json create mode 100644 .devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json create mode 100644 .devcontainer/config/frontend/public/static/keycloak/keycloak.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 10041feb7..331efd3a3 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -45,13 +45,12 @@ RUN if [ "${NODE_VERSION}" != "none" ]; then \ RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - - # Add the Yarn package repository to APT sources - RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list - - # Update APT package lists and install required packages - RUN sudo apt-get update && \ - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ - postgresql-contrib \ - default-jre \ - libasound2 libgconf-2-4 libgtk-3-dev libnotify-dev libnss3 libxss1 xvfb - +# Add the Yarn package repository to APT sources +RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + +# Update APT package lists and install required packages +RUN sudo apt-get update && \ + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + postgresql-contrib \ + default-jre \ + libasound2 libgconf-2-4 libgtk-3-dev libnotify-dev libnss3 libxss1 xvfb build-essential python3-dev cython3 chromium diff --git a/.devcontainer/config/api/dotenv b/.devcontainer/config/api/dotenv index 79ef723e9..7ac9313a3 100644 --- a/.devcontainer/config/api/dotenv +++ b/.devcontainer/config/api/dotenv @@ -180,3 +180,18 @@ DATABASE_NAME=postgres SQLALCHEMY_POOL_SIZE=10 SQLALCHEMY_MAX_OVERFLOW=20 SQLALCHEMY_ECHO=False + +############################# JWT Config Variables ############################ + +JWT_OIDC_AUDIENCE=theq-queue-management-api +JWT_OIDC_WELL_KNOWN_CONFIG=https://dev.loginproxy.gov.bc.ca/auth/realms/servicebc/.well-known/openid-configuration + +########################## Exam Management Variables ########################## +#BCMP_BASE_URL= +#BCMP_AUTH_TOKEN= +#BCMP_USER= +#MINIO_ACCESS_KEY= +#MINIO_HOST= +#MINIO_BUCKET= +#MINIO_USE_SECURE='1' +#MINIO_SECRET_KEY= diff --git a/.devcontainer/config/appointment-frontend/dotenv.local b/.devcontainer/config/appointment-frontend/dotenv.local new file mode 100644 index 000000000..708b567ab --- /dev/null +++ b/.devcontainer/config/appointment-frontend/dotenv.local @@ -0,0 +1,3 @@ +VUE_APP_PATH=/ +VUE_APP_GOOGLE_STATIC_MAP_API_KEY=replace_key_here +VUE_APP_ROOT_API=http://localhost:5000/api/v1 diff --git a/.devcontainer/config/appointment-frontend/public/config/configuration.json b/.devcontainer/config/appointment-frontend/public/config/configuration.json new file mode 100644 index 000000000..bf5e3a8b3 --- /dev/null +++ b/.devcontainer/config/appointment-frontend/public/config/configuration.json @@ -0,0 +1,15 @@ +{ + "KEYCLOAK_CONFIG_URL": "./public/config/kc/keycloak-public.json", + "VUE_APP_ROOT_API": "http://localhost:5000/api/v1", + "hideBCServicesCard": false, + "BCEIDRegistrationUrl": "", + "BCServicesCardUrl": "", + "disableSms": false, + "VUE_APP_FEEDBACK_API": "http://localhost:5001/api/v1", + "FEEDBACK_SERVICE_CHANNEL": "online", + "FEEDBACK_ENABLED": true, + "VUE_APP_HEADER_MSG": "Unlinked Text {link}Link 1 Text{link} Unlinked Text {link}Link 2 Text{link} Unlinked Text {link}Link 3 Text", + "VUE_APP_HEADER_LINKS": "https://example.com/link1{link}https://example.com/link2{link}https://example.com/link3", + "VUE_APP_FOOTER_MSG": "Unlinked Text {link}Link 1 Text{link} Unlinked Text {link}Link 2 Text{link} Unlinked Text {link}Link 3 Text", + "VUE_APP_FOOTER_LINKS": "https://example.com/link1{link}https://example.com/link2{link}https://example.com/link3" +} diff --git a/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json b/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json new file mode 100644 index 000000000..43d6027a9 --- /dev/null +++ b/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json @@ -0,0 +1,8 @@ +{ + "realm": "servicebc", + "auth-server-url": "https://dev.loginproxy.gov.bc.ca/auth/", + "ssl-required": "external", + "resource": "theq-frontend", + "public-client": "true", + "confidential-port": 0 +} diff --git a/.devcontainer/config/frontend/public/static/keycloak/keycloak.json b/.devcontainer/config/frontend/public/static/keycloak/keycloak.json new file mode 100644 index 000000000..43d6027a9 --- /dev/null +++ b/.devcontainer/config/frontend/public/static/keycloak/keycloak.json @@ -0,0 +1,8 @@ +{ + "realm": "servicebc", + "auth-server-url": "https://dev.loginproxy.gov.bc.ca/auth/", + "ssl-required": "external", + "resource": "theq-frontend", + "public-client": "true", + "confidential-port": 0 +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f5f79191f..96d96af77 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,17 +20,38 @@ "dockerComposeFile": "docker-compose.yml", // Add the extensions you want installed when the container is created. - "extensions": [ - "bierner.markdown-preview-github-styles", - "dbaeumer.vscode-eslint", - "github.vscode-pull-request-github", - "ms-python.python", - "ms-python.vscode-pylance", - "mtxr.sqltools", - "mtxr.sqltools-driver-pg", - "octref.vetur", - "sonarsource.sonarlint-vscode" - ], + "customizations": { + "vscode": { + "extensions": [ + "bierner.markdown-preview-github-styles", + "dbaeumer.vscode-eslint", + "github.vscode-pull-request-github", + "ms-python.python", + "ms-python.pylint", + "ms-python.vscode-pylance", + "ms-python.debugpy", + "mtxr.sqltools", + "mtxr.sqltools-driver-pg", + "octref.vetur", + "sonarsource.sonarlint-vscode" + ], + "settings": { + "python.languageServer": "Pylance", + "sqltools.connections": [ + { + "database": "postgres", + "driver": "PostgreSQL", + "name": "Container database", + "password": "postgres", + "port": 5432, + "previewLimit": 50, + "server": "localhost", + "username": "postgres" + } + ] + } + } + }, // Use 'forwardPorts' to make a list of ports inside the container // available locally: "forwardPorts": [ @@ -57,7 +78,7 @@ }, "10000": { "label": "Xpra (Cypress)", - "onAutoForward": "silent", + "onAutoForward": "silent" } }, // Use 'postCreateCommand' to run commands after the container is created. @@ -66,27 +87,5 @@ // https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", "service": "app", - // Set *default* container specific settings.json values on container create. - "settings": { - "python.languageServer": "Pylance", - "python.linting.enabled": true, - "python.linting.pylintEnabled": true, - "xxxpython.linting.pylintPath": "/usr/local/py-utils/bin/pylint", - "python.pythonPath": "/usr/local/bin/python", - // "python.testing.pytestPath": "/usr/local/py-utils/bin/pytest", - "sonarlint.ls.javaHome": "/usr/lib/jvm/default-java", - "sqltools.connections": [ - { - "database": "postgres", - "driver": "PostgreSQL", - "name": "Container database", - "password": "postgres", - "port": 5432, - "previewLimit": 50, - "server": "localhost", - "username": "postgres" - } - ] - }, "workspaceFolder": "/workspace" } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index ffb699272..46aeccde4 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -12,8 +12,6 @@ # License for the specific language governing permissions and limitations under # the License. -version: '3.7' - services: # The container that contains all the code in the repository, plus the # installations of Python requirements and Node packages. @@ -29,7 +27,7 @@ services: # update to the runtime version. The list of versions available is at # https://hub.docker.com/_/microsoft-vscode-devcontainers # https://mcr.microsoft.com/v2/vscode/devcontainers/python/tags/list - VARIANT: '0.201.2-3.8' + VARIANT: '3.10-bullseye' # Node.js version to install NODE_VERSION: '20.11.1' @@ -77,16 +75,6 @@ services: volumes: - 'postgres-data:/var/lib/postgresql/data' -# keycloak: -# environment: -# KEYCLOAK_PASSWORD: 'admin' -# KEYCLOAK_USER: 'admin' -# -# image: 'jboss/keycloak:9.0.3' -# -# ports: -# - '8085:8080' - # Display X clients (like Cypress) on an X server that uses Xpra to forward # the display. Set the XPRA_PASSWORD password below and use a browser outside # Docker to view the clients: diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index f5b0ff8c3..73e0aabd2 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -21,12 +21,26 @@ COLOR_DEFAULT='\033[0m' COLOR_FAILURE='\033[0;31m' +# Log the output to make it easier to find when things go wrong. + +LOGDIR=".devcontainer/logs" +LOGFILE="$LOGDIR/error.log" + +if [ ! -d $LOGDIR ]; then + mkdir -p $LOGDIR +fi + +touch $LOGFILE + +# Redirect stderr to both the logfile and terminal using tee +exec 2> >(tee -a $LOGFILE >&2) + # Echo a string in red. # # Parameter: string # echo_failure () { - echo -e "$COLOR_FAILURE$*$COLOR_DEFAULT" + echo -e "$COLOR_FAILURE$*$COLOR_DEFAULT" | tee -a $LOGFILE >&2 } # If a configuration file doesn't exist then create a default file. @@ -79,83 +93,72 @@ check_setting () { # Dependency Installations ############################################################################### -# Log the output to make it easier to find when things go wrong. -LOGDIR=.devcontainer/logs -if [ ! -d $LOGDIR ]; then - mkdir $LOGDIR -fi - -# To save time do the installations in parallel. -( - cd api - rm -rf env - python -m venv env - source env/bin/activate - python -m pip install --upgrade pip -q - pip install -r requirements_dev.txt --progress-bar off +install_api_deps () { + ( + cd api + # Remove old virtual environment and create a new one + rm -rf env || handle_error "Failed to remove old virtual environment." + sudo python3 -m venv env || handle_error "Failed to create virtual environment." - # Install newman so that the postman tests can be run on the command line. - echo Installing newman - cd postman - rm -rf node_modules - npm install newman -) |& tee $LOGDIR/api.log & + # Activate the virtual environment and install dependencies + source env/bin/activate || handle_error "Failed to activate virtual environment." + sudo chown -R $USER:$USER /workspace + python -m pip install --upgrade pip -q || handle_error "Failed to upgrade pip." + pip install -r requirements_dev.txt --progress-bar off || handle_error "Failed to install dependencies." + ) +} # If NPM output is piped into a commmand, it does not display any indication of # progress. Use "script" to make NPM think it is running on a TTY. -script -fq -c "( - cd appointment-frontend - rm -rf node_modules - npm install - $(npm bin)/cypress install -)" |& tee $LOGDIR/appointment-frontend.log & - -#( -# cd feedback-api -# rm -rf env -# python -m venv env -# source env/bin/activate -# python -m pip install --upgrade pip -q -# pip install -r requirements.txt --progress-bar off -#) |& tee $LOGDIR/feedback-api.log & - -script -fq -c "( - cd frontend - rm -rf node_modules - npm install -)" |& tee $LOGDIR/frontend.log & - -#( -# cd notifications-api -# rm -rf env -# python -m venv env -# source env/bin/activate -# python -m pip install --upgrade pip -q -# pip install -r requirements.txt --progress-bar off -#) |& tee $LOGDIR/notifications-api.log & - -# Wait for all the above to complete. -wait +install_appointment_frontend_deps () { + ( + cd appointment-frontend + rm -rf node_modules + rm -rf package-lock.json + npm install + npx cypress install + ) +} + +install_frontend_deps () { + ( + cd frontend + rm -rf node_modules + rm -rf package-lock.json + npm install + ) +} + +install_api_deps +install_appointment_frontend_deps +install_frontend_deps ############################################################################### # Database Bootstrapping and Setup ############################################################################### -( - cd api - source env/bin/activate - python manage.py db upgrade - - # If there is nothing in the CSR table, we're probably starting with a - # clean database and need to bootstrap it with default data. - COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ - -U postgres -c "SELECT COUNT(*) FROM csr;" -t) - if [ "$COUNT" -eq 0 ]; then - python manage.py bootstrap - python manage.py adduser - fi -) +bootstrap_database () { + ( + cd api + source env/bin/activate + python manage.py db upgrade + pip install -r requirements.txt + + # If there is nothing in the CSR table, we're probably starting with a + # clean database and need to bootstrap it with default data. + python manage.py migrate + read -p "Enter your IDIR to check if db is bootstrapped: " SEARCH_USER + COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ + -U postgres -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';" -t) + if [ "$COUNT" -eq 0 ]; then + python manage.py bootstrap + echo "$SEARCH_USER" | python manage.py adduser + fi + ) +} + +bootstrap_database ############################################################################### # Configuration Files Setup and Checking @@ -170,5 +173,16 @@ check_setting api/.env JWT_OIDC_WELL_KNOWN_CONFIG copy_config .devcontainer/config/api/client_secrets/secrets.json \ api/client_secrets/secrets.json +copy_config .devcontainer/config/frontend/public/static/keycloak/keycloak.json \ + frontend/public/static/keycloak/keycloak.json + copy_config .devcontainer/config/frontend/public/config/configuration.json \ frontend/public/config/configuration.json + +copy_config .devcontainer/config/appointment-frontend/dotenv.local appointment-frontend/.env.local + +copy_config .devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json \ + appointment-frontend/public/config/kc/keycloak-public.json + +copy_config .devcontainer/config/appointment-frontend/public/config/configuration.json \ + appointment-frontend/public/config/configuration.json From 730382100565ccb7d67388153a4ab521ae4c1de5 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Sat, 26 Jul 2025 13:01:57 -0700 Subject: [PATCH 02/81] wip --- .devcontainer/docker-compose.yml | 10 ++++++++-- .devcontainer/postCreateCommand.sh | 28 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 46aeccde4..a7f1a739e 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -56,8 +56,11 @@ services: network_mode: 'service:db' volumes: - - 'x11:/tmp/.X11-unix:rw' - - '..:/workspace:cached' + - x11:/tmp/.X11-unix:rw + - ..:/workspace:cached + - api-env:/workspace/api/env + - appointment-frontend-node-modules:/workspace/appointment-frontend/node_modules + - frontend-node-modules:/workspace/frontend/node_modules # Run PostgreSQL as the database used by the API. Keep the version in sync # with what is used in the runtime deployments. @@ -99,3 +102,6 @@ services: volumes: postgres-data: x11: + api-env: + appointment-frontend-node-modules: + frontend-node-modules: diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 73e0aabd2..59463e6d4 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -93,19 +93,19 @@ check_setting () { # Dependency Installations ############################################################################### - install_api_deps () { ( cd api - # Remove old virtual environment and create a new one - rm -rf env || handle_error "Failed to remove old virtual environment." - sudo python3 -m venv env || handle_error "Failed to create virtual environment." + # Ensure the env directory is owned by the current user + if [ -d env ]; then + sudo chown -R $(id -u):$(id -g) env + fi + python3 -m venv env || echo_failure "Failed to create virtual environment." # Activate the virtual environment and install dependencies - source env/bin/activate || handle_error "Failed to activate virtual environment." - sudo chown -R $USER:$USER /workspace - python -m pip install --upgrade pip -q || handle_error "Failed to upgrade pip." - pip install -r requirements_dev.txt --progress-bar off || handle_error "Failed to install dependencies." + source env/bin/activate || echo_failure "Failed to activate virtual environment." + python -m pip install --upgrade pip -q || echo_failure "Failed to upgrade pip." + pip install -r requirements_dev.txt --progress-bar off || echo_failure "Failed to install dependencies." ) } @@ -114,8 +114,10 @@ install_api_deps () { install_appointment_frontend_deps () { ( cd appointment-frontend - rm -rf node_modules - rm -rf package-lock.json + # Ensure the node_modules directory is owned by the current user + if [ -d node_modules ]; then + sudo chown -R $(id -u):$(id -g) node_modules + fi npm install npx cypress install ) @@ -124,8 +126,10 @@ install_appointment_frontend_deps () { install_frontend_deps () { ( cd frontend - rm -rf node_modules - rm -rf package-lock.json + # Ensure the node_modules directory is owned by the current user + if [ -d node_modules ]; then + sudo chown -R $(id -u):$(id -g) node_modules + fi npm install ) } From 5008a328aa375ef6e1b4e89e58137b9bfc52b86c Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 24 Mar 2026 21:59:34 -0700 Subject: [PATCH 03/81] Upgrade to Python 3.11 --- .devcontainer/docker-compose.yml | 2 +- api/requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index a7f1a739e..91624e0ad 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -27,7 +27,7 @@ services: # update to the runtime version. The list of versions available is at # https://hub.docker.com/_/microsoft-vscode-devcontainers # https://mcr.microsoft.com/v2/vscode/devcontainers/python/tags/list - VARIANT: '3.10-bullseye' + VARIANT: '3.11-bullseye' # Node.js version to install NODE_VERSION: '20.11.1' diff --git a/api/requirements.txt b/api/requirements.txt index a6568b19c..b21530711 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -54,13 +54,13 @@ marshmallow==3.21.2 marshmallow-sqlalchemy==0.26.1 mccabe==0.6.1 minio==7.0.4 -numpy==1.21.0 +numpy==1.23.2 oauthlib==3.2.2 packaging==24.0 pip==24.0 pkgutil_resolve_name==1.3.10 pluggy==0.13.1 -psycopg2-binary==2.9.1 +psycopg2-binary==2.9.11 py==1.11.0 pyasn1==0.6.0 pycontracts3==7.2 From 3acebb4d89a842183fe6e11ee922a56c9bc9364a Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 24 Mar 2026 22:56:31 -0700 Subject: [PATCH 04/81] Replace eventlet with gevent --- api/gunicorn_config.py | 2 +- api/qsystem.py | 4 ++-- api/requirements.txt | 2 -- api/wsgi.py | 6 +++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/gunicorn_config.py b/api/gunicorn_config.py index 1535f858c..858b42444 100644 --- a/api/gunicorn_config.py +++ b/api/gunicorn_config.py @@ -14,7 +14,7 @@ workers = int(os.environ.get('GUNICORN_PROCESSES', '1')) threads = int(os.environ.get('GUNICORN_THREADS', '1')) -worker_class = 'eventlet' +worker_class = 'gevent' worker_connections = 500 timeout = 10 keepalive = 20 diff --git a/api/qsystem.py b/api/qsystem.py index a3cb4a72a..50c537b4c 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -86,12 +86,12 @@ def time_string(): cors_allowed_origins=application.config['CORS_ALLOWED_ORIGINS']) if application.config['ACTIVE_MQ_URL'] is not None: - socketio.init_app(application, async_mode='eventlet', + socketio.init_app(application, async_mode='gevent', message_queue=application.config['ACTIVE_MQ_URL'], redis_options={'REDIS_OPTIONS'}, path='/api/v1/socket.io') else: - socketio.init_app(application, path='/api/v1/socket.io') + socketio.init_app(application, async_mode='gevent',path='/api/v1/socket.io') if application.config['CORS_ALLOWED_ORIGINS'] is not None: CORS(application, supports_credentials=True, origins=application.config['CORS_ALLOWED_ORIGINS']) diff --git a/api/requirements.txt b/api/requirements.txt index b21530711..17edbcc73 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -12,9 +12,7 @@ certifi==2024.7.4 charset-normalizer==3.3.2 click==7.1.2 decorator==5.0.9 -dnspython==2.6.1 ecdsa==0.19.0 -eventlet==0.35.2 filelock==3.0.12 Flask==1.1.4 Flask-Admin==1.5.8 diff --git a/api/wsgi.py b/api/wsgi.py index 8f3afd9af..c1bcc0d22 100644 --- a/api/wsgi.py +++ b/api/wsgi.py @@ -1,7 +1,7 @@ -import eventlet +from gevent import monkey -#Monkey patch to allow for async actions (aka multiple workers) -eventlet.monkey_patch() +# Monkey patch to allow for async actions (aka multiple workers) +monkey.patch_all() from qsystem import application, socketio From c489165cb57dd3aebe2903272e4db2f0075a2558 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 24 Mar 2026 23:06:00 -0700 Subject: [PATCH 05/81] Update snowplow-tracker and remove unused dependencies --- api/app/utilities/snowplow.py | 4 ++-- api/requirements.txt | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/api/app/utilities/snowplow.py b/api/app/utilities/snowplow.py index 57175204e..d32703929 100644 --- a/api/app/utilities/snowplow.py +++ b/api/app/utilities/snowplow.py @@ -344,8 +344,8 @@ def make_tracking_call(schema, citizen, office, agent): # Set up core Snowplow environment if SnowPlow.call_snowplow_flag: s = Subject() # .set_platform("app") - e = AsyncEmitter(SnowPlow.sp_endpoint, on_failure=SnowPlow.failure, protocol="https") - t = Tracker(e, encode_base64=False, app_id=SnowPlow.sp_appid, namespace=SnowPlow.sp_namespace) + e = AsyncEmitter(SnowPlow.sp_endpoint, on_failure=SnowPlow.failure, protocol="https", method="get") + t = Tracker(namespace=SnowPlow.sp_namespace, emitters=e, encode_base64=False, app_id=SnowPlow.sp_appid) # Set up the correct level of logging. print_flag = application.config['PRINT_ENABLE'] diff --git a/api/requirements.txt b/api/requirements.txt index 17edbcc73..02d37361e 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -16,7 +16,6 @@ ecdsa==0.19.0 filelock==3.0.12 Flask==1.1.4 Flask-Admin==1.5.8 -Flask-Cache==0.13.1 Flask-Caching==1.11.1 Flask-Compress==1.9.0 Flask-Cors==5.0.0 @@ -29,7 +28,6 @@ flask-restx==0.5.1 Flask-Script==2.0.6 Flask-SocketIO==5.1.0 Flask-SQLAlchemy==2.5.1 -future==0.18.2 gevent==24.2.1 greenlet==3.0.3 gunicorn==22.0.0 @@ -52,16 +50,13 @@ marshmallow==3.21.2 marshmallow-sqlalchemy==0.26.1 mccabe==0.6.1 minio==7.0.4 -numpy==1.23.2 oauthlib==3.2.2 packaging==24.0 pip==24.0 pkgutil_resolve_name==1.3.10 pluggy==0.13.1 psycopg2-binary==2.9.11 -py==1.11.0 pyasn1==0.6.0 -pycontracts3==7.2 pylint==2.7.2 pyparsing==3.0.7 pysnow==0.7.17 @@ -82,8 +77,7 @@ rpds-py==0.18.1 rsa==4.9 setuptools==70.0.0 simple-websocket==1.0.0 -six==1.16.0 -snowplow-tracker==0.9.1 +snowplow-tracker==1.1.0 SQLAlchemy==1.3.24 SQLAlchemy-Continuum==1.3.11 SQLAlchemy-Utc==0.12.0 From fa3abd22353643e3ad0c3154a6e99c80da820048 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 24 Mar 2026 23:45:09 -0700 Subject: [PATCH 06/81] Remove servicenow feedback --- api/app/resources/theq/feedback.py | 83 ++---------------------------- api/requirements.txt | 20 +++---- 2 files changed, 15 insertions(+), 88 deletions(-) diff --git a/api/app/resources/theq/feedback.py b/api/app/resources/theq/feedback.py index 31629c991..6d6096d6e 100644 --- a/api/app/resources/theq/feedback.py +++ b/api/app/resources/theq/feedback.py @@ -19,7 +19,6 @@ import urllib.request import urllib.parse import os -import pysnow import requests import json from app.utilities.auth_util import Role, has_any_role @@ -31,7 +30,6 @@ class Feedback(Resource): feedback_destinations = application.config['THEQ_FEEDBACK'] flag_teams = "TEAMS" in feedback_destinations - flag_service_now = "SERVICENOW" in feedback_destinations flag_rocket_chat = "ROCKETCHAT" in feedback_destinations @jwt.has_one_of_roles([Role.internal_user.value]) @@ -46,26 +44,21 @@ def post(self): return {"message": "Must provide message to send as feedback"}, 422 teams_result = None - service_now_result = None rocket_chat_result = None if self.flag_teams: teams_result = Feedback.send_to_teams(feedback_message) - if self.flag_service_now: - service_now_result = Feedback.send_to_service_now(feedback_message) - if self.flag_rocket_chat: rocket_chat_result = Feedback.send_to_rocket_chat(feedback_message) - # Calculate return message as combination of teams and service now results. - result = Feedback.combine_results("Teams: ", teams_result, "Service Now: ", service_now_result) - result = Feedback.combine_results("", result, "Rocket Chat: ", rocket_chat_result) + # Calculate return message as combination + result = Feedback.combine_results("Teams: ", teams_result, "Rocket Chat: ", rocket_chat_result) return result @staticmethod def send_to_teams(feedback_message): - + url = application.config['TEAMS_URL'] if url is None: @@ -88,77 +81,9 @@ def send_to_teams(feedback_message): else: return {"message": "error", "http_code": resp.getcode()}, 400 - @staticmethod - def send_to_service_now(params): - - instance = application.config['SERVICENOW_INSTANCE'] - user = application.config['SERVICENOW_USER'] - password = application.config['SERVICENOW_PASSWORD'] - table = application.config['SERVICENOW_TABLE'] - tenant = application.config['SERVICENOW_TENANT'] - assign_group = application.config['SERVICENOW_ASSIGN_GROUP'] - - if instance is None: - return {"message": "SERVICENOW_INSTANCE is not set"}, 400 - if user is None: - return {"message": "SERVICENOW_USER is not set"}, 400 - if password is None: - return {"message": "SERVICENOW_PASSWORD is not set"}, 400 - if table is None: - return {"message": "SERVICENOW_TABLE is not set"}, 400 - if tenant is None: - return {"message": "SERVICENOW_TENANT is not set"}, 400 - if assign_group is None: - return {"message": "SERVICENOW_ASSIGN_GROUP is not set"}, 400 - - # Generate Service Now incident. - # NOTE: Automatic email gets sent to the assignment group below - # ONLY IF the email of the SERVICENOW_USER IS NOT the same - # as the email of this assignment group. - - c = pysnow.Client(instance = instance, user=user, password=password) - incident = c.resource(api_path=table) - csr = Feedback.extract_string(params, "Username: ", "\n", 0) - ticket = Feedback.extract_string(params, "Ticket Number: ","\n", 0) - msg = Feedback.extract_string(params, "Message: ", "", 50) - short_desc = "TheQ Feedback (CSR: " + csr + "; Ticket: " + ticket + "; Msg: " + msg + ")" - - # Create new record depending on tenant value. - if len(tenant) != 0: - new_record = { - 'caller_id': user, - 'category': 'Inquiry / Help', - 'cmdb_ci': 'CFMS', - 'u_tenant': tenant, - 'impact': '2 - Some Customers', - 'urgency': '2 - High', - 'priority': 'High', - 'short_description': short_desc, - 'description': params, - 'assignment_group': assign_group - } - else: - new_record = { - 'category': 'Inquiry / Help', - 'cmdb_ci': 'CFMS', - 'impact': '2 - Some Customers', - 'urgency': '2 - High', - 'priority': 'High', - 'short_description': short_desc, - 'description': params, - 'assignment_group': assign_group - } - - result = incident.create(payload=new_record) - - if '201' in str(result): - return {"status": "Success"}, 201 - else: - return {"message": "Service Now incident not created"}, 400 - @staticmethod def send_to_rocket_chat(feedback_message): - + url = application.config['ROCKET_CHAT_URL'] if url is None: diff --git a/api/requirements.txt b/api/requirements.txt index 02d37361e..2d49cf63e 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,7 +1,7 @@ alembic==1.5.2 amqp==5.2.0 aniso8601==9.0.1 -astroid==2.5.8 +astroid==4.0.4 async-timeout==4.0.3 attrs==23.2.0 bidict==0.23.1 @@ -12,6 +12,7 @@ certifi==2024.7.4 charset-normalizer==3.3.2 click==7.1.2 decorator==5.0.9 +dill==0.4.1 ecdsa==0.19.0 filelock==3.0.12 Flask==1.1.4 @@ -37,7 +38,7 @@ ijson==2.6.1 importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 -isort==5.13.2 +isort==8.0.1 itsdangerous==1.1.0 Jinja2==2.11.3 jsonschema==4.22.0 @@ -52,15 +53,15 @@ mccabe==0.6.1 minio==7.0.4 oauthlib==3.2.2 packaging==24.0 -pip==24.0 pkgutil_resolve_name==1.3.10 -pluggy==0.13.1 +platformdirs==4.9.4 +pluggy==1.6.0 psycopg2-binary==2.9.11 pyasn1==0.6.0 -pylint==2.7.2 +Pygments==2.19.2 +pylint==4.0.5 pyparsing==3.0.7 -pysnow==0.7.17 -pytest==6.2.4 +pytest==9.0.2 python-dateutil==2.9.0.post0 python-dotenv==0.17.1 python-editor==1.0.4 @@ -75,18 +76,19 @@ requests==2.32.2 requests-oauthlib==1.3.1 rpds-py==0.18.1 rsa==4.9 -setuptools==70.0.0 simple-websocket==1.0.0 +six==1.17.0 snowplow-tracker==1.1.0 SQLAlchemy==1.3.24 SQLAlchemy-Continuum==1.3.11 SQLAlchemy-Utc==0.12.0 SQLAlchemy-Utils==0.41.2 toml==0.10.2 +tomlkit==0.14.0 +typing_extensions==4.15.0 urllib3==1.26.19 vine==5.1.0 Werkzeug==1.0.1 -wheel==0.41.2 wrapt==1.12.1 wsproto==1.2.0 WTForms==3.0.0 From 2385479f128d9512eb60015643ace687b66a6405 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 24 Mar 2026 23:48:24 -0700 Subject: [PATCH 07/81] Update python-dotenv to version 1.2.2 --- api/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/requirements.txt b/api/requirements.txt index 2d49cf63e..10a5f7d10 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -63,7 +63,7 @@ pylint==4.0.5 pyparsing==3.0.7 pytest==9.0.2 python-dateutil==2.9.0.post0 -python-dotenv==0.17.1 +python-dotenv==1.2.2 python-editor==1.0.4 python-engineio==4.9.0 python-jose==3.3.0 From bb774bd9b6a1652663de3dfb662fb028538cc5a7 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 10:40:22 -0700 Subject: [PATCH 08/81] Remove dependency on Flask-Script --- .devcontainer/postCreateCommand.sh | 2 +- api/config.py | 4 - api/manage.py | 1969 ++++++++++++++-------------- api/qsystem.py | 1 - api/requirements.txt | 1 - 5 files changed, 984 insertions(+), 993 deletions(-) diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 59463e6d4..0edc59e6c 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -151,7 +151,7 @@ bootstrap_database () { # If there is nothing in the CSR table, we're probably starting with a # clean database and need to bootstrap it with default data. - python manage.py migrate + python manage.py migrate_db read -p "Enter your IDIR to check if db is bootstrapped: " SEARCH_USER COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ -U postgres -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';" -t) diff --git a/api/config.py b/api/config.py index 40ef36ba9..223e10d4e 100644 --- a/api/config.py +++ b/api/config.py @@ -184,7 +184,6 @@ class BaseConfig(object): class LocalConfig(BaseConfig): DEBUG = True TESTING = False - ENV = 'dev' USE_HTTPS = False PREFERRED_URL_SCHEME = 'http' @@ -227,7 +226,6 @@ class DevelopmentConfig(BaseConfig): DEBUG = True REDIS_DEBUG = True TESTING = False - ENV = 'dev' USE_HTTPS = True PREFERRED_URL_SCHEME = 'https' @@ -240,7 +238,6 @@ class TestConfig(BaseConfig): DEBUG = True REDIS_DEBUG = True TESTING = False - ENV = 'test' USE_HTTPS = True PREFERRED_URL_SCHEME = 'https' BCMP_BASE_URL = os.getenv('BCMP_BASE_URL') @@ -252,7 +249,6 @@ class ProductionConfig(BaseConfig): DEBUG = True REDIS_DEBUG = True TESTING = False - ENV = 'production' USE_HTTPS = True PREFERRED_URL_SCHEME = 'https' BCMP_BASE_URL = os.getenv('BCMP_BASE_URL') diff --git a/api/manage.py b/api/manage.py index f14f60c96..c15f051f5 100644 --- a/api/manage.py +++ b/api/manage.py @@ -7,1043 +7,1040 @@ Usage: python manage.py db - python manage.py migrate + python manage.py migrate_db python manage.py bootstrap # ***will wipe your database*** ''' import logging -from flask_script import Command, Manager -from flask_migrate import Migrate, MigrateCommand, upgrade +import click +import flask_migrate as fm from qsystem import db, application from app.models import theq, bookings -migrate = Migrate(application, db) -manager = Manager(application) +# Alias for Flask CLI auto-discovery (FLASK_APP=manage) +app = application -# Defining String constants to appease SonarQube -back_office_const = "Back Office" -chalkboard_const = "Loves using chalk boards to communicate to examinees" +fm.Migrate(application, db) -class AddAdminCsr(Command): - ''' - Adds a new CSR who is an administrator user. This is used to add - developers so that they can log into the Q locally. - ''' - def run(self): - ''' - Adds a CSR with full administrative permissions. - ''' - username = input('Your IDIR, to create an admin CSR account: ') - if username != '': - db.session.add(theq.CSR( - username=username, - office_id=AddAdminCsr.__get_office('Victoria').office_id, - role_id=AddAdminCsr.__get_role('SUPPORT').role_id, - counter_id=AddAdminCsr.__get_counter('Quick Trans').counter_id, - receptionist_ind=1, - csr_state_id=AddAdminCsr.__get_csr_state('Logout') - .csr_state_id - )) - db.session.commit() +@application.cli.group('db') +def db_cli(): + '''Database migration commands.''' + pass - @staticmethod - def __get_counter(counter_name): - ''' Gets the named Counter. ''' - return theq.Counter.query.filter( - theq.Counter.counter_name == counter_name).first() +@db_cli.command() +@click.option('-m', '--message', default=None, help='Revision message') +def migrate(message): + '''Create a new migration revision.''' + fm.migrate(message=message) - @staticmethod - def __get_csr_state(csr_state_name): - ''' Gets the named CSR State. ''' - return theq.CSRState.query.filter( - theq.CSRState.csr_state_name == csr_state_name).first() +@db_cli.command() +@click.argument('revision', default='head') +def upgrade(revision): + '''Upgrade the database.''' + fm.upgrade(revision=revision) - @staticmethod - def __get_office(office_name): - ''' Gets the named Office. ''' - return theq.Office.query.filter( - theq.Office.office_name == office_name).first() +@db_cli.command() +@click.argument('revision', default='-1') +def downgrade(revision): + '''Downgrade the database.''' + fm.downgrade(revision=revision) - @staticmethod - def __get_role(role_code): - ''' Gets the Role identified by its code. ''' - return theq.Role.query.filter( - theq.Role.role_code == role_code).first() +@db_cli.command() +def current(): + '''Show the current revision.''' + fm.current() +@db_cli.command() +def history(): + '''Show migration history.''' + fm.history() -class Bootstrap(Command): - ''' - Sets up the database with enough information to do development or - run demos. - ''' +# Defining String constants to appease SonarQube +back_office_const = "Back Office" +chalkboard_const = "Loves using chalk boards to communicate to examinees" - def run(self): - ''' - Bootstraps the database by deleting all table rows and - populating with enough data to run the application for - development or demos. - ''' +def _get_counter(counter_name): + return theq.Counter.query.filter( + theq.Counter.counter_name == counter_name).first() - logging.info('Clearing out all models') - theq.Period.query.delete() - theq.PeriodState.query.delete() - theq.ServiceReq.query.delete() - theq.SRState.query.delete() - bookings.Appointment.query.delete() - bookings.Booking.query.delete() - bookings.Exam.query.delete() - theq.Citizen.query.delete() - theq.CitizenState.query.delete() - theq.CSR.query.delete() - theq.CSRState.query.delete() - bookings.Booking.query.delete() - bookings.ExamType.query.delete() - bookings.Room.query.delete() - bookings.Invigilator.query.delete() - theq.TimeSlot.query.delete() - theq.Office.query.delete() - theq.SmartBoard.query.delete() - theq.Counter.query.delete() - theq.Role.query.delete() - theq.Permission.query.delete() - theq.Service.query.filter_by(actual_service_ind=1).delete() - theq.Service.query.delete() - theq.Channel.query.delete() - bookings.Booking.query.delete() - theq.Timezone.query.delete() +def _get_csr_state(csr_state_name): + return theq.CSRState.query.filter( + theq.CSRState.csr_state_name == csr_state_name).first() - logging.info('Starting to bootstrap data') +def _get_office(office_name): + return theq.Office.query.filter( + theq.Office.office_name == office_name).first() - # -- Channels -------------------------------------------------- - logging.info('--> Channels') - db.session.add(theq.Channel(channel_name='In Person')) - db.session.add(theq.Channel(channel_name='Phone')) - db.session.add(theq.Channel(channel_name='Back Office')) - db.session.add(theq.Channel(channel_name='Email/Fax/Mail')) - db.session.add(theq.Channel(channel_name='CATs Assist')) - db.session.add(theq.Channel(channel_name='Mobile Assist')) - db.session.commit() +def _get_role(role_code): + return theq.Role.query.filter( + theq.Role.role_code == role_code).first() - # -- Roles ----------------------------------------------------- - logging.info('--> Roles') - role_csr = theq.Role( - role_code='CSR', - role_desc='Customer Service Representative' - ) - role_ga = theq.Role( - role_code='GA', - role_desc='Government Agent' - ) - role3 = theq.Role( - role_code='HELPDESK', - role_desc='Help Desk Functions' - ) - role4 = theq.Role( - role_code='SUPPORT', - role_desc='All Administrative Functions' - ) - role5 = theq.Role( - role_code='ANALYTICS', - role_desc='Analytics Team to update Services per Office' - ) - db.session.add(role_csr) - db.session.add(role_ga) - db.session.add(role3) - db.session.add(role4) - db.session.add(role5) - db.session.commit() - - # -- Period States --------------------------------------------- - logging.info('--> Period States') - db.session.add(theq.PeriodState( - ps_name='Waiting', - ps_desc='Waiting in line to see a CSR, after a ticket has been ' - 'created for them. The time they are in this state is the Citizen ' - 'Wait Time.', - ps_number=1 - )) - db.session.add(theq.PeriodState( - ps_name='Ticket Creation', - ps_desc='A receptionist is creating a service request / ticket ' - 'for the citizen. This is the first state a citizen will be in. ' - 'The time they are in this state is the CSR prep time.', - ps_number=2 - )) - db.session.add(theq.PeriodState( - ps_name='Invited', - ps_desc='Has been called from the waiting area to be served. The ' - 'time they are in this state is the time it takes them to walk ' - 'from the waiting area, to the CSR, until the CSR starts to serve ' - 'them.', - ps_number=4 - )) - db.session.add(theq.PeriodState( - ps_name='Being Served', - ps_desc='Is being served by a CSR. The time they are in this ' - 'state is the Service time.', - ps_number=7 - )) - db.session.add(theq.PeriodState( - ps_name='On hold', - ps_desc='Has been placed on hold by a CSR. The time they are in ' - 'this state is the Hold time.', - ps_number=11 +@application.cli.command('adduser') +def add_admin_csr(): + '''Adds a new CSR who is an administrator user.''' + username = input('Your IDIR, to create an admin CSR account: ') + if username != '': + db.session.add(theq.CSR( + username=username, + office_id=_get_office('Victoria').office_id, + role_id=_get_role('SUPPORT').role_id, + counter_id=_get_counter('Quick Trans').counter_id, + receptionist_ind=1, + csr_state_id=_get_csr_state('Logout').csr_state_id )) db.session.commit() - # -- Smart Boards ---------------------------------------------- - logging.info('--> Smart Boards') - smartboard_call_name = theq.SmartBoard(sb_type='callbyname') - smartboard_call_ticket = theq.SmartBoard(sb_type='callbyticket') - smartboard_no_call = theq.SmartBoard(sb_type='nocallonsmartboard') - db.session.add(smartboard_call_name) - db.session.add(smartboard_call_ticket) - db.session.add(smartboard_no_call) - db.session.commit() - # -- Citizen States -------------------------------------------- - logging.info('--> Citizen States') - db.session.add(theq.CitizenState( - cs_state_name='Active', - cs_state_desc='Citizen is active, a ticket is being or has been ' - 'created for them' - )) - db.session.add(theq.CitizenState( - cs_state_name='Received Services', - cs_state_desc='Citizen left after receiving services' - )) - db.session.add(theq.CitizenState( - cs_state_name='Left before receiving services', - cs_state_desc='Citizen left, after ticket creation, before ' - 'service was started for them' - )) - db.session.add(theq.CitizenState( - cs_state_name='Appointment booked', - cs_state_desc='Citizen has booked an appointment' - )) - db.session.commit() +@application.cli.command('bootstrap') +def bootstrap(): + '''Sets up the database with enough information to do development or run demos.''' - # -- CSR States ------------------------------------------------ - logging.info('--> CSR States') - csr_state_logout = theq.CSRState( - csr_state_name='Logout', - csr_state_desc='Logged out' - ) - csr_state2 = theq.CSRState( - csr_state_name='Login', - csr_state_desc='Logged in' - ) - csr_state3 = theq.CSRState( - csr_state_name='Break', - csr_state_desc='Currently on break' - ) - csr_state4 = theq.CSRState( - csr_state_name='Serving', - csr_state_desc='Serving a citizen' - ) - csr_state5 = theq.CSRState( - csr_state_name=back_office_const, - csr_state_desc="Currently doing back office work" - ) - db.session.add(csr_state_logout) - db.session.add(csr_state2) - db.session.add(csr_state3) - db.session.add(csr_state4) - db.session.add(csr_state5) - db.session.commit() + logging.info('Clearing out all models') + theq.Period.query.delete() + theq.PeriodState.query.delete() + theq.ServiceReq.query.delete() + theq.SRState.query.delete() + bookings.Appointment.query.delete() + bookings.Booking.query.delete() + bookings.Exam.query.delete() + theq.Citizen.query.delete() + theq.CitizenState.query.delete() + theq.CSR.query.delete() + theq.CSRState.query.delete() + bookings.Booking.query.delete() + bookings.ExamType.query.delete() + bookings.Room.query.delete() + bookings.Invigilator.query.delete() + theq.TimeSlot.query.delete() + theq.Office.query.delete() + theq.SmartBoard.query.delete() + theq.Counter.query.delete() + theq.Role.query.delete() + theq.Permission.query.delete() + theq.Service.query.filter_by(actual_service_ind=1).delete() + theq.Service.query.delete() + theq.Channel.query.delete() + bookings.Booking.query.delete() + theq.Timezone.query.delete() - # -- Service Request States ------------------------------------ - logging.info('--> Service Request States') - db.session.add(theq.SRState( - sr_code='Pending', - sr_state_desc='Service Request is pending, citizen has not ' - 'started receiving services yet.' - )) - db.session.add(theq.SRState( - sr_code='Active', - sr_state_desc='Service Request is active. A citizen has ' - 'started being served.' - )) - db.session.add(theq.SRState( - sr_code='Complete', - sr_state_desc='The service has been received for this Service ' - 'Request.' - )) - db.session.commit() + logging.info('Starting to bootstrap data') - # -- Services (Categories) ------------------------------------- - logging.info('--> Services (Categories)') - category_msp = theq.Service( - external_service_name='Medical Services Plan', - service_code='MSP', - service_name='MSP', - service_desc='Medical Services Plan', - prefix='A', - display_dashboard_ind=0, - actual_service_ind=0 - ) - category_ptax = theq.Service( - external_service_name='Rural Property Tax', - service_code='PTAX', - service_name='Property Tax', - service_desc='Property Tax', - prefix='A', - display_dashboard_ind=0, - actual_service_ind=0 - ) - category_back_office = theq.Service( - service_code = back_office_const, - service_name = back_office_const, - service_desc = back_office_const, - prefix = "B", - display_dashboard_ind = 0, - actual_service_ind = 0 - ) - category_exams = theq.Service( - service_code='Exams (code)', - service_name='Exams (name)', - service_desc='Exams (desc)', - prefix='E', - display_dashboard_ind=0, - actual_service_ind=0 - ) - category_icbc = theq.Service( - service_code='ICBC (code)', - service_name='ICBC (name)', - service_desc='ICBC (desc)', - prefix='A', - display_dashboard_ind=0, - actual_service_ind=0 - ) - db.session.add(category_msp) - db.session.add(category_ptax) - db.session.add(category_back_office) - db.session.add(category_exams) - db.session.add(category_icbc) - db.session.commit() + # -- Channels -------------------------------------------------- + logging.info('--> Channels') + db.session.add(theq.Channel(channel_name='In Person')) + db.session.add(theq.Channel(channel_name='Phone')) + db.session.add(theq.Channel(channel_name='Back Office')) + db.session.add(theq.Channel(channel_name='Email/Fax/Mail')) + db.session.add(theq.Channel(channel_name='CATs Assist')) + db.session.add(theq.Channel(channel_name='Mobile Assist')) + db.session.commit() - # -- Services -------------------------------------------------- - logging.info('--> Services') - service_msp6 = theq.Service( - service_code='MSP - 006', - service_name='Payment - MSP', - service_desc='MSP- SC686, SC1089 -Pay direct payment, employer ' - 'payment', - parent_id=category_msp.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='Medical Services Plan Payment' - ) - service_ptax4 = theq.Service( - service_code='PTAX - 004', - service_name='Other - PTAX', - service_desc='PTax/RPT - Providing information, forms, searches, ' - 'tax clearance certificate, address changes, add new owner, ' - 'extensions, forfeiture status, tax search, etc.', - parent_id=category_ptax.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1 - ) - service_ptax1 = theq.Service( - service_code='PTAX - 001', - service_name='Deferment Application', - service_desc='PTax/RPT - Process application - new and renewal, ' - 'post note, etc.', - parent_id=category_ptax.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='Rural Property Tax - Deferment Application' - ) - service_ptax2 = theq.Service( - service_code='PTAX - 002', - service_name='Deferment Payment', - service_desc='PTax/RPT - Full or Partial deferment account ' - 'payment', - parent_id=category_ptax.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='Rural Property Tax - Deferment Payment' - ) - service_msp1 = theq.Service( - service_code='MSP - 001', - service_name='Account Enquiry/Update', - service_desc='MSP-Address or family changes, personal information ' - 'updates, general status enquiries, billing information from ' - 'Biller Direct, immigration documents to HIBC, needs PHN, etc.', - parent_id=category_msp.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='Medical Services Plan Account Update' - ) - service_msp2 = theq.Service( - service_code='MSP - 002', - service_name='BCSC Non Photo', - service_desc='MSP- SC2607 RAPID ordering, status enquiry, address ' - 'update, also for the non photo form process when photo eligible, ' - 'etc.', - parent_id=category_msp.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='BC Services Card - Non-photo Card' - ) - service_bo1 = theq.Service( - service_code='Back Office - 001', - service_name='Batching', - service_desc='Batching', - parent_id=category_back_office.service_id, - prefix='B', - display_dashboard_ind=0, - actual_service_ind=1 - ) - service_bo2 = theq.Service( - service_code='Back Office - 002', - service_name='Cash Out', - service_desc='Cash Out', - parent_id=category_back_office.service_id, - prefix='B', - display_dashboard_ind=0, - actual_service_ind=1 - ) - service_exams = theq.Service( - service_code='Exams - 001', - service_name='Exam Management', - service_desc='ITA or PEST -Checking for expired Exams, contacting ' - 'ITA or PEST program, mailing back ITA or shredding expired PEST ' - 'Exams, etc.', - parent_id=category_exams.service_id, - prefix='E', - display_dashboard_ind=1, - actual_service_ind=1 - ) - service_dlkt = theq.Service( - service_code='ICBC - 008', - service_name='Knowledge Test Set-Up/Result', - service_desc='ICBC - Knowledge Test Set up/Result: KT - Either put ' - 'it on hold or end it after the test set up. When client returns, ' - 'you either resume or create a new one. ', - parent_id=category_icbc.service_id, - prefix='A', - display_dashboard_ind=1, - actual_service_ind=1, - external_service_name='ICBC - Drivers Licence Knowledge Test', - is_dlkt='YES' - ) - db.session.add(service_bo1) - db.session.add(service_bo2) - db.session.add(service_msp1) - db.session.add(service_msp2) - db.session.add(service_msp6) - db.session.add(service_ptax1) - db.session.add(service_ptax2) - db.session.add(service_ptax4) - db.session.add(service_exams) - db.session.add(service_dlkt) - db.session.commit() + # -- Roles ----------------------------------------------------- + logging.info('--> Roles') + role_csr = theq.Role( + role_code='CSR', + role_desc='Customer Service Representative' + ) + role_ga = theq.Role( + role_code='GA', + role_desc='Government Agent' + ) + role3 = theq.Role( + role_code='HELPDESK', + role_desc='Help Desk Functions' + ) + role4 = theq.Role( + role_code='SUPPORT', + role_desc='All Administrative Functions' + ) + role5 = theq.Role( + role_code='ANALYTICS', + role_desc='Analytics Team to update Services per Office' + ) + db.session.add(role_csr) + db.session.add(role_ga) + db.session.add(role3) + db.session.add(role4) + db.session.add(role5) + db.session.commit() - # -- Counters -------------------------------------------------- - logging.info('--> Counters') - qt_counter = theq.Counter( - counter_name='Quick Trans', - counter_id=1, - ) - counter = theq.Counter( - counter_name='Counter', - counter_id=2 - ) - db.session.add(qt_counter) - db.session.add(counter) - db.session.commit() + # -- Period States --------------------------------------------- + logging.info('--> Period States') + db.session.add(theq.PeriodState( + ps_name='Waiting', + ps_desc='Waiting in line to see a CSR, after a ticket has been ' + 'created for them. The time they are in this state is the Citizen ' + 'Wait Time.', + ps_number=1 + )) + db.session.add(theq.PeriodState( + ps_name='Ticket Creation', + ps_desc='A receptionist is creating a service request / ticket ' + 'for the citizen. This is the first state a citizen will be in. ' + 'The time they are in this state is the CSR prep time.', + ps_number=2 + )) + db.session.add(theq.PeriodState( + ps_name='Invited', + ps_desc='Has been called from the waiting area to be served. The ' + 'time they are in this state is the time it takes them to walk ' + 'from the waiting area, to the CSR, until the CSR starts to serve ' + 'them.', + ps_number=4 + )) + db.session.add(theq.PeriodState( + ps_name='Being Served', + ps_desc='Is being served by a CSR. The time they are in this ' + 'state is the Service time.', + ps_number=7 + )) + db.session.add(theq.PeriodState( + ps_name='On hold', + ps_desc='Has been placed on hold by a CSR. The time they are in ' + 'this state is the Hold time.', + ps_number=11 + )) + db.session.commit() - # -- Timezones ------------------------------------------------- - logging.info('--> Timezones') - timezone_one = theq.Timezone(timezone_name='America/Vancouver') - timezone_two = theq.Timezone(timezone_name='America/Dawson_Creek') - timezone_three = theq.Timezone(timezone_name='America/Edmonton') - timezone_four = theq.Timezone(timezone_name='America/Creston') - db.session.add(timezone_one) - db.session.add(timezone_two) - db.session.add(timezone_three) - db.session.add(timezone_four) - db.session.commit() + # -- Smart Boards ---------------------------------------------- + logging.info('--> Smart Boards') + smartboard_call_name = theq.SmartBoard(sb_type='callbyname') + smartboard_call_ticket = theq.SmartBoard(sb_type='callbyticket') + smartboard_no_call = theq.SmartBoard(sb_type='nocallonsmartboard') + db.session.add(smartboard_call_name) + db.session.add(smartboard_call_ticket) + db.session.add(smartboard_no_call) + db.session.commit() - # -- Offices --------------------------------------------------- - logging.info('--> Offices') - office_test = theq.Office( - office_name='Test Office', - office_number=999, - sb_id=smartboard_call_ticket.sb_id, - exams_enabled_ind=1, - timezone_id=timezone_one.timezone_id, - appointments_enabled_ind=1, - latitude=48.458359, - longitude=-123.377106, - office_appointment_message='Test Message', - appointments_days_limit=30, - appointment_duration=30, - max_person_appointment_per_day=10, - civic_address='4000 Seymour', - telephone='999-999-9999', - online_status='SHOW' - ) - office_test.counters.append(counter) - office_test.counters.append(qt_counter) + # -- Citizen States -------------------------------------------- + logging.info('--> Citizen States') + db.session.add(theq.CitizenState( + cs_state_name='Active', + cs_state_desc='Citizen is active, a ticket is being or has been ' + 'created for them' + )) + db.session.add(theq.CitizenState( + cs_state_name='Received Services', + cs_state_desc='Citizen left after receiving services' + )) + db.session.add(theq.CitizenState( + cs_state_name='Left before receiving services', + cs_state_desc='Citizen left, after ticket creation, before ' + 'service was started for them' + )) + db.session.add(theq.CitizenState( + cs_state_name='Appointment booked', + cs_state_desc='Citizen has booked an appointment' + )) + db.session.commit() - office_100 = theq.Office( - office_name='100 Mile House', - office_number=61, - sb_id=smartboard_no_call.sb_id, - exams_enabled_ind=0, - timezone_id=timezone_four.timezone_id, - appointments_enabled_ind=1, - latitude=51.644, - longitude=-121.295, - appointments_days_limit=30, - appointment_duration=30, - max_person_appointment_per_day=1, - civic_address='100 Mile House, BC', - telephone='999-999-9999', - online_status='SHOW' - ) - office_100.counters.append(counter) - office_100.counters.append(qt_counter) + # -- CSR States ------------------------------------------------ + logging.info('--> CSR States') + csr_state_logout = theq.CSRState( + csr_state_name='Logout', + csr_state_desc='Logged out' + ) + csr_state2 = theq.CSRState( + csr_state_name='Login', + csr_state_desc='Logged in' + ) + csr_state3 = theq.CSRState( + csr_state_name='Break', + csr_state_desc='Currently on break' + ) + csr_state4 = theq.CSRState( + csr_state_name='Serving', + csr_state_desc='Serving a citizen' + ) + csr_state5 = theq.CSRState( + csr_state_name=back_office_const, + csr_state_desc="Currently doing back office work" + ) + db.session.add(csr_state_logout) + db.session.add(csr_state2) + db.session.add(csr_state3) + db.session.add(csr_state4) + db.session.add(csr_state5) + db.session.commit() - office_victoria = theq.Office( - office_name='Victoria', - office_number=94, - sb_id=smartboard_call_name.sb_id, - exams_enabled_ind=0, - timezone_id=timezone_one.timezone_id, - appointments_enabled_ind=1, - latitude=51.644, - longitude=-121.295, - appointments_days_limit=30, - appointment_duration=30, - max_person_appointment_per_day=1, - civic_address='Victoria, BC', - telephone='999-999-9999', - online_status='SHOW' - ) - office_victoria.counters.append(counter) - office_victoria.counters.append(qt_counter) + # -- Service Request States ------------------------------------ + logging.info('--> Service Request States') + db.session.add(theq.SRState( + sr_code='Pending', + sr_state_desc='Service Request is pending, citizen has not ' + 'started receiving services yet.' + )) + db.session.add(theq.SRState( + sr_code='Active', + sr_state_desc='Service Request is active. A citizen has ' + 'started being served.' + )) + db.session.add(theq.SRState( + sr_code='Complete', + sr_state_desc='The service has been received for this Service ' + 'Request.' + )) + db.session.commit() - office_pesticide_office = theq.Office( - office_name='Pesticide Offsite', - office_number=997, - sb_id=smartboard_call_name.sb_id, - exams_enabled_ind=1, - timezone_id=timezone_one.timezone_id, - appointments_enabled_ind=0, - appointments_days_limit=30, - appointment_duration=30, - max_person_appointment_per_day=1, - online_status='HIDE' - ) - office_pesticide_office.counters.append(counter) - office_pesticide_office.counters.append(qt_counter) + # -- Services (Categories) ------------------------------------- + logging.info('--> Services (Categories)') + category_msp = theq.Service( + external_service_name='Medical Services Plan', + service_code='MSP', + service_name='MSP', + service_desc='Medical Services Plan', + prefix='A', + display_dashboard_ind=0, + actual_service_ind=0 + ) + category_ptax = theq.Service( + external_service_name='Rural Property Tax', + service_code='PTAX', + service_name='Property Tax', + service_desc='Property Tax', + prefix='A', + display_dashboard_ind=0, + actual_service_ind=0 + ) + category_back_office = theq.Service( + service_code = back_office_const, + service_name = back_office_const, + service_desc = back_office_const, + prefix = "B", + display_dashboard_ind = 0, + actual_service_ind = 0 + ) + category_exams = theq.Service( + service_code='Exams (code)', + service_name='Exams (name)', + service_desc='Exams (desc)', + prefix='E', + display_dashboard_ind=0, + actual_service_ind=0 + ) + category_icbc = theq.Service( + service_code='ICBC (code)', + service_name='ICBC (name)', + service_desc='ICBC (desc)', + prefix='A', + display_dashboard_ind=0, + actual_service_ind=0 + ) + db.session.add(category_msp) + db.session.add(category_ptax) + db.session.add(category_back_office) + db.session.add(category_exams) + db.session.add(category_icbc) + db.session.commit() - db.session.add(office_test) - db.session.add(office_100) - db.session.add(office_victoria) - db.session.add(office_pesticide_office) - db.session.commit() + # -- Services -------------------------------------------------- + logging.info('--> Services') + service_msp6 = theq.Service( + service_code='MSP - 006', + service_name='Payment - MSP', + service_desc='MSP- SC686, SC1089 -Pay direct payment, employer ' + 'payment', + parent_id=category_msp.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='Medical Services Plan Payment' + ) + service_ptax4 = theq.Service( + service_code='PTAX - 004', + service_name='Other - PTAX', + service_desc='PTax/RPT - Providing information, forms, searches, ' + 'tax clearance certificate, address changes, add new owner, ' + 'extensions, forfeiture status, tax search, etc.', + parent_id=category_ptax.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1 + ) + service_ptax1 = theq.Service( + service_code='PTAX - 001', + service_name='Deferment Application', + service_desc='PTax/RPT - Process application - new and renewal, ' + 'post note, etc.', + parent_id=category_ptax.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='Rural Property Tax - Deferment Application' + ) + service_ptax2 = theq.Service( + service_code='PTAX - 002', + service_name='Deferment Payment', + service_desc='PTax/RPT - Full or Partial deferment account ' + 'payment', + parent_id=category_ptax.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='Rural Property Tax - Deferment Payment' + ) + service_msp1 = theq.Service( + service_code='MSP - 001', + service_name='Account Enquiry/Update', + service_desc='MSP-Address or family changes, personal information ' + 'updates, general status enquiries, billing information from ' + 'Biller Direct, immigration documents to HIBC, needs PHN, etc.', + parent_id=category_msp.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='Medical Services Plan Account Update' + ) + service_msp2 = theq.Service( + service_code='MSP - 002', + service_name='BCSC Non Photo', + service_desc='MSP- SC2607 RAPID ordering, status enquiry, address ' + 'update, also for the non photo form process when photo eligible, ' + 'etc.', + parent_id=category_msp.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='BC Services Card - Non-photo Card' + ) + service_bo1 = theq.Service( + service_code='Back Office - 001', + service_name='Batching', + service_desc='Batching', + parent_id=category_back_office.service_id, + prefix='B', + display_dashboard_ind=0, + actual_service_ind=1 + ) + service_bo2 = theq.Service( + service_code='Back Office - 002', + service_name='Cash Out', + service_desc='Cash Out', + parent_id=category_back_office.service_id, + prefix='B', + display_dashboard_ind=0, + actual_service_ind=1 + ) + service_exams = theq.Service( + service_code='Exams - 001', + service_name='Exam Management', + service_desc='ITA or PEST -Checking for expired Exams, contacting ' + 'ITA or PEST program, mailing back ITA or shredding expired PEST ' + 'Exams, etc.', + parent_id=category_exams.service_id, + prefix='E', + display_dashboard_ind=1, + actual_service_ind=1 + ) + service_dlkt = theq.Service( + service_code='ICBC - 008', + service_name='Knowledge Test Set-Up/Result', + service_desc='ICBC - Knowledge Test Set up/Result: KT - Either put ' + 'it on hold or end it after the test set up. When client returns, ' + 'you either resume or create a new one. ', + parent_id=category_icbc.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1, + external_service_name='ICBC - Drivers Licence Knowledge Test', + is_dlkt='YES' + ) + db.session.add(service_bo1) + db.session.add(service_bo2) + db.session.add(service_msp1) + db.session.add(service_msp2) + db.session.add(service_msp6) + db.session.add(service_ptax1) + db.session.add(service_ptax2) + db.session.add(service_ptax4) + db.session.add(service_exams) + db.session.add(service_dlkt) + db.session.commit() - # -- Time Slots ------------------------------------------------ - logging.info('--> Time Slots') - db.session.add(theq.TimeSlot( - start_time='09:00:00-07:00', - end_time='10:00:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_100.office_id - )) - db.session.add(theq.TimeSlot( - start_time='10:30:00-07:00', - end_time='11:00:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_100.office_id - )) - db.session.add(theq.TimeSlot( - start_time='13:00:00-07:00', - end_time='13:30:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_100.office_id - )) - db.session.add(theq.TimeSlot( - start_time='13:30:00-07:00', - end_time='16:30:00-07:00', - no_of_slots=0, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_100.office_id - )) - db.session.add(theq.TimeSlot( - start_time='09:00:00-07:00', - end_time='09:45:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_victoria.office_id - )) - db.session.add(theq.TimeSlot( - start_time='10:30:00-07:00', - end_time='11:15:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_victoria.office_id - )) - db.session.add(theq.TimeSlot( - start_time='11:00:00-07:00', - end_time='11:45:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_victoria.office_id - )) - db.session.add(theq.TimeSlot( - start_time='15:00:00-07:00', - end_time='15:45:00-07:00', - no_of_slots=1, - day_of_week=[ - 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' - ], - office_id=office_victoria.office_id - )) - db.session.commit() + # -- Counters -------------------------------------------------- + logging.info('--> Counters') + qt_counter = theq.Counter( + counter_name='Quick Trans', + counter_id=1, + ) + counter = theq.Counter( + counter_name='Counter', + counter_id=2 + ) + db.session.add(qt_counter) + db.session.add(counter) + db.session.commit() - # -- CSRs ------------------------------------------------------ - logging.info('--> CSRs') - db.session.add(theq.CSR( - username='cfms-postman-operator', - office_id=office_test.office_id, - role_id=role_ga.role_id, - counter_id=qt_counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=0, - pesticide_designate=0, - finance_designate=0, - ita2_designate=0 - )) - db.session.add(theq.CSR( - username='cfms-postman-non-operator', - office_id=office_test.office_id, - role_id=role_csr.role_id, - counter_id=counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=0, - pesticide_designate=0, - finance_designate=0, - ita2_designate=0 - )) - db.session.add(theq.CSR( - username='demoga', - office_id=office_test.office_id, - role_id=role_ga.role_id, - counter_id=counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=1, - pesticide_designate=1, - finance_designate=1, - ita2_designate=1 - )) - db.session.add(theq.CSR( - username='democsr', - office_id=office_test.office_id, - role_id=role_csr.role_id, - counter_id=counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=0, - pesticide_designate=0, - finance_designate=0, - ita2_designate=0 - )) - db.session.add(theq.CSR( - username='admin', - office_id=office_test.office_id, - role_id=role4.role_id, - counter_id=counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=1, - pesticide_designate=1, - finance_designate=1, - ita2_designate=1 - )) - db.session.add(theq.CSR( - username='user', - office_id=office_test.office_id, - role_id=role_csr.role_id, - counter_id=counter.counter_id, - receptionist_ind=1, - deleted=None, - csr_state_id=csr_state_logout.csr_state_id, - office_manager=0, - pesticide_designate=0, - finance_designate=0, - ita2_designate=0 - )) - db.session.commit() + # -- Timezones ------------------------------------------------- + logging.info('--> Timezones') + timezone_one = theq.Timezone(timezone_name='America/Vancouver') + timezone_two = theq.Timezone(timezone_name='America/Dawson_Creek') + timezone_three = theq.Timezone(timezone_name='America/Edmonton') + timezone_four = theq.Timezone(timezone_name='America/Creston') + db.session.add(timezone_one) + db.session.add(timezone_two) + db.session.add(timezone_three) + db.session.add(timezone_four) + db.session.commit() - # -- Office Services ------------------------------------------- - logging.info('--> Office Services') - office_test.services.append(category_back_office) - office_test.services.append(category_msp) - office_test.services.append(category_ptax) - office_test.services.append(category_exams) - office_test.services.append(category_icbc) - office_test.services.append(service_bo1) - office_test.services.append(service_bo2) - office_test.services.append(service_msp1) - office_test.services.append(service_msp2) - office_test.services.append(service_msp6) - office_test.services.append(service_ptax1) - office_test.services.append(service_ptax2) - office_test.services.append(service_ptax4) - office_test.services.append(service_exams) - office_test.services.append(service_dlkt) + # -- Offices --------------------------------------------------- + logging.info('--> Offices') + office_test = theq.Office( + office_name='Test Office', + office_number=999, + sb_id=smartboard_call_ticket.sb_id, + exams_enabled_ind=1, + timezone_id=timezone_one.timezone_id, + appointments_enabled_ind=1, + latitude=48.458359, + longitude=-123.377106, + office_appointment_message='Test Message', + appointments_days_limit=30, + appointment_duration=30, + max_person_appointment_per_day=10, + civic_address='4000 Seymour', + telephone='999-999-9999', + online_status='SHOW' + ) + office_test.counters.append(counter) + office_test.counters.append(qt_counter) - office_victoria.services.append(category_back_office) - office_victoria.services.append(category_msp) - office_victoria.services.append(category_icbc) - office_victoria.services.append(service_bo1) - office_victoria.services.append(service_bo2) - office_victoria.services.append(service_msp1) - office_victoria.services.append(service_msp2) - office_victoria.services.append(service_msp6) - office_victoria.services.append(service_dlkt) + office_100 = theq.Office( + office_name='100 Mile House', + office_number=61, + sb_id=smartboard_no_call.sb_id, + exams_enabled_ind=0, + timezone_id=timezone_four.timezone_id, + appointments_enabled_ind=1, + latitude=51.644, + longitude=-121.295, + appointments_days_limit=30, + appointment_duration=30, + max_person_appointment_per_day=1, + civic_address='100 Mile House, BC', + telephone='999-999-9999', + online_status='SHOW' + ) + office_100.counters.append(counter) + office_100.counters.append(qt_counter) - office_100.services.append(category_back_office) - office_100.services.append(category_ptax) - office_100.services.append(category_icbc) - office_100.services.append(service_bo1) - office_100.services.append(service_bo2) - office_100.services.append(service_ptax1) - office_100.services.append(service_ptax2) - office_100.services.append(service_ptax4) - office_100.services.append(service_dlkt) - db.session.commit() + office_victoria = theq.Office( + office_name='Victoria', + office_number=94, + sb_id=smartboard_call_name.sb_id, + exams_enabled_ind=0, + timezone_id=timezone_one.timezone_id, + appointments_enabled_ind=1, + latitude=51.644, + longitude=-121.295, + appointments_days_limit=30, + appointment_duration=30, + max_person_appointment_per_day=1, + civic_address='Victoria, BC', + telephone='999-999-9999', + online_status='SHOW' + ) + office_victoria.counters.append(counter) + office_victoria.counters.append(qt_counter) - # -- Booking / Rooms ------------------------------------------- - logging.info('--> Booking: Rooms') - db.session.add(bookings.Room( - office_id=office_test.office_id, - room_name='Boardroom 1', - capacity=25, - color='#EFD469' - )) - db.session.add(bookings.Room( - office_id=office_test.office_id, - room_name='Turquoise W-135', - capacity=25, - color='#EFD469' - )) - db.session.commit() + office_pesticide_office = theq.Office( + office_name='Pesticide Offsite', + office_number=997, + sb_id=smartboard_call_name.sb_id, + exams_enabled_ind=1, + timezone_id=timezone_one.timezone_id, + appointments_enabled_ind=0, + appointments_days_limit=30, + appointment_duration=30, + max_person_appointment_per_day=1, + online_status='HIDE' + ) + office_pesticide_office.counters.append(counter) + office_pesticide_office.counters.append(qt_counter) - # -- Booking / Invigilators ------------------------------------ - logging.info('--> Booking: Invigilators') - db.session.add(bookings.Invigilator( - invigilator_name='Homer Simpson', - office_id=office_test.office_id, - invigilator_notes='He works in a nuclear power plant.', - contact_phone='2502084247', - contact_email='homer.j.simpson@gmail.com', - contract_number='c-000001', - contract_expiry_date='2018-11-30' - )) - db.session.add(bookings.Invigilator( - invigilator_name='Lisa Simpson', - office_id=office_test.office_id, - invigilator_notes='She plays the sax-a-ma-phone during exams', - contact_phone='555-555-5555', - contact_email='lisasimpsonesq@gmail.com', - contract_number='c-000002', - contract_expiry_date='2018-12-31' - )) - db.session.add(bookings.Invigilator( - invigilator_name='Bart Simpson', - office_id=office_test.office_id, - invigilator_notes='Loves using chalk boards to communicate to ' - 'examinees', - contact_phone='555-555-5555', - contact_email='bartwuzhere@gmail.com', - contract_number='c-000003', - contract_expiry_date='2019-01-31' - )) - db.session.add(bookings.Invigilator( - invigilator_name='Pest 1', - office_id=office_pesticide_office.office_id, - invigilator_notes='The first pest invigilator is not a pest', - contact_phone='555-555-5555', - contact_email='pest_invigilator_1@gmail.com', - contract_number='c-000003', - contract_expiry_date='2019-01-31' - )) - db.session.add(bookings.Invigilator( - invigilator_name='Pest 2', - office_id=office_pesticide_office.office_id, - invigilator_notes='The second pest invigilator is a pest', - contact_phone='555-555-5555', - contact_email='pest_invigilator_2@gmail.com', - contract_number='c-000003', - contract_expiry_date='2019-01-31' - )) - db.session.commit() + db.session.add(office_test) + db.session.add(office_100) + db.session.add(office_victoria) + db.session.add(office_pesticide_office) + db.session.commit() - # -- Booking / Exam Types -------------------------------------- - logging.info('--> Booking: Exam Types') - db.session.add(bookings.ExamType( - exam_type_name='COFQ - 3HR Group Exam', - exam_color='#FF69B4', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=1, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='COFQ - 3HR Single Exam', - exam_color='#FF69B4', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='COFQ - 3HR Single Exam - Own Reader', - exam_color='#FF69B4', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='COFQ - 3HR Single Exam - SBC Reader', - exam_color='#FF69B4', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='COFQ - 3HR Single Exam - Time Extension', - exam_color='#FF69B4', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='IPSE - 4HR Group Exam', - exam_color='#FFD701', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=1, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='IPSE - 4HR Single Exam', - exam_color='#FFD701', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='IPSE - 4HR Single Exam - Own Reader', - exam_color='#FFD701', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='IPSE - 4HR Single Exam - SBC Reader', - exam_color='#FFD701', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='IPSE - 4HR Single Exam - Time Extension', - exam_color='#FFD701', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='SLE - 3HR Group Exam', - exam_color='#8FBC8F', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=1, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='SLE - 3HR Single Exam', - exam_color='#8FBC8F', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='SLE - 3HR Single Exam - Own Reader', - exam_color='#8FBC8F', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='SLE - 3HR Single Exam - SBC Reader', - exam_color='#8FBC8F', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='SLE - 3HR Single Exam - Time Extension', - exam_color='#8FBC8F', - number_of_hours=3, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='Monthly Session Exam', - exam_color='#FFFFFF', - number_of_hours=4, - method_type='Written', - ita_ind=1, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='Veterinary Exam', - exam_color='#FFFFFF', - number_of_hours=2, - method_type='written', - ita_ind=0, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='Milk Grader', - exam_color='#FFFFFF', - number_of_hours=2, - method_type='written', - ita_ind=0, - group_exam_ind=0, - pesticide_exam_ind=0 - )) - db.session.add(bookings.ExamType( - exam_type_name='Industrial Vegetation', - exam_color='#FFFFFF', - number_of_hours=3, - method_type='written', - ita_ind=0, - group_exam_ind=0, - pesticide_exam_ind=1 - )) - db.session.add(bookings.ExamType( - exam_type_name='Structural-General', - exam_color='#FFFFFF', - number_of_hours=3, - method_type='written', - ita_ind=0, - group_exam_ind=0, - pesticide_exam_ind=1 - )) - db.session.add(bookings.ExamType( - exam_type_name='Dispenser-Commercial', - exam_color='#FFFFFF', - number_of_hours=1, - method_type='written', - ita_ind=0, - group_exam_ind=0, - pesticide_exam_ind=1 - )) - db.session.add(bookings.ExamType( - exam_type_name='Group Environment Exam', - exam_color='#FFFFFF', - number_of_hours=0, - method_type='written', - ita_ind=0, - group_exam_ind=1, - pesticide_exam_ind=0 - )) - db.session.commit() + # -- Time Slots ------------------------------------------------ + logging.info('--> Time Slots') + db.session.add(theq.TimeSlot( + start_time='09:00:00-07:00', + end_time='10:00:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_100.office_id + )) + db.session.add(theq.TimeSlot( + start_time='10:30:00-07:00', + end_time='11:00:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_100.office_id + )) + db.session.add(theq.TimeSlot( + start_time='13:00:00-07:00', + end_time='13:30:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_100.office_id + )) + db.session.add(theq.TimeSlot( + start_time='13:30:00-07:00', + end_time='16:30:00-07:00', + no_of_slots=0, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_100.office_id + )) + db.session.add(theq.TimeSlot( + start_time='09:00:00-07:00', + end_time='09:45:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_victoria.office_id + )) + db.session.add(theq.TimeSlot( + start_time='10:30:00-07:00', + end_time='11:15:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_victoria.office_id + )) + db.session.add(theq.TimeSlot( + start_time='11:00:00-07:00', + end_time='11:45:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_victoria.office_id + )) + db.session.add(theq.TimeSlot( + start_time='15:00:00-07:00', + end_time='15:45:00-07:00', + no_of_slots=1, + day_of_week=[ + 'Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday' + ], + office_id=office_victoria.office_id + )) + db.session.commit() + + # -- CSRs ------------------------------------------------------ + logging.info('--> CSRs') + db.session.add(theq.CSR( + username='cfms-postman-operator', + office_id=office_test.office_id, + role_id=role_ga.role_id, + counter_id=qt_counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=0, + pesticide_designate=0, + finance_designate=0, + ita2_designate=0 + )) + db.session.add(theq.CSR( + username='cfms-postman-non-operator', + office_id=office_test.office_id, + role_id=role_csr.role_id, + counter_id=counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=0, + pesticide_designate=0, + finance_designate=0, + ita2_designate=0 + )) + db.session.add(theq.CSR( + username='demoga', + office_id=office_test.office_id, + role_id=role_ga.role_id, + counter_id=counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=1, + pesticide_designate=1, + finance_designate=1, + ita2_designate=1 + )) + db.session.add(theq.CSR( + username='democsr', + office_id=office_test.office_id, + role_id=role_csr.role_id, + counter_id=counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=0, + pesticide_designate=0, + finance_designate=0, + ita2_designate=0 + )) + db.session.add(theq.CSR( + username='admin', + office_id=office_test.office_id, + role_id=role4.role_id, + counter_id=counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=1, + pesticide_designate=1, + finance_designate=1, + ita2_designate=1 + )) + db.session.add(theq.CSR( + username='user', + office_id=office_test.office_id, + role_id=role_csr.role_id, + counter_id=counter.counter_id, + receptionist_ind=1, + deleted=None, + csr_state_id=csr_state_logout.csr_state_id, + office_manager=0, + pesticide_designate=0, + finance_designate=0, + ita2_designate=0 + )) + db.session.commit() + + # -- Office Services ------------------------------------------- + logging.info('--> Office Services') + office_test.services.append(category_back_office) + office_test.services.append(category_msp) + office_test.services.append(category_ptax) + office_test.services.append(category_exams) + office_test.services.append(category_icbc) + office_test.services.append(service_bo1) + office_test.services.append(service_bo2) + office_test.services.append(service_msp1) + office_test.services.append(service_msp2) + office_test.services.append(service_msp6) + office_test.services.append(service_ptax1) + office_test.services.append(service_ptax2) + office_test.services.append(service_ptax4) + office_test.services.append(service_exams) + office_test.services.append(service_dlkt) + + office_victoria.services.append(category_back_office) + office_victoria.services.append(category_msp) + office_victoria.services.append(category_icbc) + office_victoria.services.append(service_bo1) + office_victoria.services.append(service_bo2) + office_victoria.services.append(service_msp1) + office_victoria.services.append(service_msp2) + office_victoria.services.append(service_msp6) + office_victoria.services.append(service_dlkt) + + office_100.services.append(category_back_office) + office_100.services.append(category_ptax) + office_100.services.append(category_icbc) + office_100.services.append(service_bo1) + office_100.services.append(service_bo2) + office_100.services.append(service_ptax1) + office_100.services.append(service_ptax2) + office_100.services.append(service_ptax4) + office_100.services.append(service_dlkt) + db.session.commit() - logging.info('--> Booking: Exams - No exams added') - logging.info('--> Booking: Appointments - No appointments added') + # -- Booking / Rooms ------------------------------------------- + logging.info('--> Booking: Rooms') + db.session.add(bookings.Room( + office_id=office_test.office_id, + room_name='Boardroom 1', + capacity=25, + color='#EFD469' + )) + db.session.add(bookings.Room( + office_id=office_test.office_id, + room_name='Turquoise W-135', + capacity=25, + color='#EFD469' + )) + db.session.commit() + # -- Booking / Invigilators ------------------------------------ + logging.info('--> Booking: Invigilators') + db.session.add(bookings.Invigilator( + invigilator_name='Homer Simpson', + office_id=office_test.office_id, + invigilator_notes='He works in a nuclear power plant.', + contact_phone='2502084247', + contact_email='homer.j.simpson@gmail.com', + contract_number='c-000001', + contract_expiry_date='2018-11-30' + )) + db.session.add(bookings.Invigilator( + invigilator_name='Lisa Simpson', + office_id=office_test.office_id, + invigilator_notes='She plays the sax-a-ma-phone during exams', + contact_phone='555-555-5555', + contact_email='lisasimpsonesq@gmail.com', + contract_number='c-000002', + contract_expiry_date='2018-12-31' + )) + db.session.add(bookings.Invigilator( + invigilator_name='Bart Simpson', + office_id=office_test.office_id, + invigilator_notes='Loves using chalk boards to communicate to ' + 'examinees', + contact_phone='555-555-5555', + contact_email='bartwuzhere@gmail.com', + contract_number='c-000003', + contract_expiry_date='2019-01-31' + )) + db.session.add(bookings.Invigilator( + invigilator_name='Pest 1', + office_id=office_pesticide_office.office_id, + invigilator_notes='The first pest invigilator is not a pest', + contact_phone='555-555-5555', + contact_email='pest_invigilator_1@gmail.com', + contract_number='c-000003', + contract_expiry_date='2019-01-31' + )) + db.session.add(bookings.Invigilator( + invigilator_name='Pest 2', + office_id=office_pesticide_office.office_id, + invigilator_notes='The second pest invigilator is a pest', + contact_phone='555-555-5555', + contact_email='pest_invigilator_2@gmail.com', + contract_number='c-000003', + contract_expiry_date='2019-01-31' + )) + db.session.commit() -class MigrateWrapper(Command): - ''' - Wraps flask_migrate's upgrade command to upgrade the database with - any changes that have been made. - ''' + # -- Booking / Exam Types -------------------------------------- + logging.info('--> Booking: Exam Types') + db.session.add(bookings.ExamType( + exam_type_name='COFQ - 3HR Group Exam', + exam_color='#FF69B4', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=1, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='COFQ - 3HR Single Exam', + exam_color='#FF69B4', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='COFQ - 3HR Single Exam - Own Reader', + exam_color='#FF69B4', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='COFQ - 3HR Single Exam - SBC Reader', + exam_color='#FF69B4', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='COFQ - 3HR Single Exam - Time Extension', + exam_color='#FF69B4', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='IPSE - 4HR Group Exam', + exam_color='#FFD701', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=1, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='IPSE - 4HR Single Exam', + exam_color='#FFD701', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='IPSE - 4HR Single Exam - Own Reader', + exam_color='#FFD701', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='IPSE - 4HR Single Exam - SBC Reader', + exam_color='#FFD701', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='IPSE - 4HR Single Exam - Time Extension', + exam_color='#FFD701', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='SLE - 3HR Group Exam', + exam_color='#8FBC8F', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=1, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='SLE - 3HR Single Exam', + exam_color='#8FBC8F', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='SLE - 3HR Single Exam - Own Reader', + exam_color='#8FBC8F', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='SLE - 3HR Single Exam - SBC Reader', + exam_color='#8FBC8F', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='SLE - 3HR Single Exam - Time Extension', + exam_color='#8FBC8F', + number_of_hours=3, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='Monthly Session Exam', + exam_color='#FFFFFF', + number_of_hours=4, + method_type='Written', + ita_ind=1, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='Veterinary Exam', + exam_color='#FFFFFF', + number_of_hours=2, + method_type='written', + ita_ind=0, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='Milk Grader', + exam_color='#FFFFFF', + number_of_hours=2, + method_type='written', + ita_ind=0, + group_exam_ind=0, + pesticide_exam_ind=0 + )) + db.session.add(bookings.ExamType( + exam_type_name='Industrial Vegetation', + exam_color='#FFFFFF', + number_of_hours=3, + method_type='written', + ita_ind=0, + group_exam_ind=0, + pesticide_exam_ind=1 + )) + db.session.add(bookings.ExamType( + exam_type_name='Structural-General', + exam_color='#FFFFFF', + number_of_hours=3, + method_type='written', + ita_ind=0, + group_exam_ind=0, + pesticide_exam_ind=1 + )) + db.session.add(bookings.ExamType( + exam_type_name='Dispenser-Commercial', + exam_color='#FFFFFF', + number_of_hours=1, + method_type='written', + ita_ind=0, + group_exam_ind=0, + pesticide_exam_ind=1 + )) + db.session.add(bookings.ExamType( + exam_type_name='Group Environment Exam', + exam_color='#FFFFFF', + number_of_hours=0, + method_type='written', + ita_ind=0, + group_exam_ind=1, + pesticide_exam_ind=0 + )) + db.session.commit() - def run(self): - ''' - Runs flask_migrate.upgrade() - ''' - logging.info('MigrateWrapper.run()') # Show if pods call it. - upgrade() + logging.info('--> Booking: Exams - No exams added') + logging.info('--> Booking: Appointments - No appointments added') -manager.add_command('adduser', AddAdminCsr()) -manager.add_command('bootstrap', Bootstrap()) -manager.add_command('db', MigrateCommand) -manager.add_command('migrate', MigrateWrapper()) +@application.cli.command('migrate_db') +def migrate_wrapper(): + '''Shortcut for "db upgrade".''' + logging.info('MigrateWrapper.run()') # Show if pods call it. + fm.upgrade() if __name__ == '__main__': - logging.info('Running the Manager') - manager.run() + application.cli() diff --git a/api/qsystem.py b/api/qsystem.py index 50c537b4c..72f594295 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -65,7 +65,6 @@ def time_string(): # Set up SQL Alchemy, caching, marshmallow db = SQLAlchemy(application) -db.init_app(application) query_limit = application.config['DB_LONG_RUNNING_QUERY'] ping_timeout_seconds = application.config['SOCKETIO_PING_TIMEOUT'] ping_interval_seconds = application.config['SOCKETIO_PING_INTERVAL'] diff --git a/api/requirements.txt b/api/requirements.txt index 10a5f7d10..2a30dee5b 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -26,7 +26,6 @@ flask-marshmallow==0.14.0 Flask-Migrate==2.7.0 Flask-Moment==0.11.0 flask-restx==0.5.1 -Flask-Script==2.0.6 Flask-SocketIO==5.1.0 Flask-SQLAlchemy==2.5.1 gevent==24.2.1 From 93a6d975cc4e3c9cfc0274d879978dd9598c74e4 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 10:51:42 -0700 Subject: [PATCH 09/81] Remove Flask-Moment --- api/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/api/requirements.txt b/api/requirements.txt index 2a30dee5b..df7abe601 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -24,7 +24,6 @@ flask-jwt-oidc==0.3.0 Flask-Login==0.5.0 flask-marshmallow==0.14.0 Flask-Migrate==2.7.0 -Flask-Moment==0.11.0 flask-restx==0.5.1 Flask-SocketIO==5.1.0 Flask-SQLAlchemy==2.5.1 From 022912f8ca442da16b749d214b2671c68ff39541 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 11:47:49 -0700 Subject: [PATCH 10/81] Flask 2 upgrade --- api/app/auth/__init__.py | 11 -------- api/app/models/theq/csr.py | 7 ++++- .../resources/bookings/exam/exam_download.py | 18 +++++-------- api/app/resources/theq/login.py | 8 ------ api/qsystem.py | 2 +- api/requirements.txt | 26 +++++++++---------- 6 files changed, 27 insertions(+), 45 deletions(-) diff --git a/api/app/auth/__init__.py b/api/app/auth/__init__.py index dff04350e..7df638897 100644 --- a/api/app/auth/__init__.py +++ b/api/app/auth/__init__.py @@ -5,15 +5,4 @@ @login_manager.user_loader def load_user(user_id): csr = CSR.query.filter_by(csr_id=int(user_id)).filter(CSR.deleted.is_(None)).first() - if not csr: - return None - - if csr.deleted is None: - csr.is_active = True - else: - csr.is_active = False - - csr.is_authenticated = True - csr.is_anonymous = False - return csr diff --git a/api/app/models/theq/csr.py b/api/app/models/theq/csr.py index dec923b68..bdfb21c47 100644 --- a/api/app/models/theq/csr.py +++ b/api/app/models/theq/csr.py @@ -13,12 +13,13 @@ limitations under the License.''' import logging +from flask_login import UserMixin from qsystem import cache, db, my_print from app.models.theq import Base from sqlalchemy import func -class CSR(Base): +class CSR(UserMixin, Base): csr_id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False) username = db.Column(db.String(150), nullable=False, unique=True) @@ -94,3 +95,7 @@ def update_user_cache(cls, userid): def get_id(self): return str(self.csr_id) + + @property + def is_active(self): + return self.deleted is None diff --git a/api/app/resources/bookings/exam/exam_download.py b/api/app/resources/bookings/exam/exam_download.py index 881b2af64..0b7fd4da2 100644 --- a/api/app/resources/bookings/exam/exam_download.py +++ b/api/app/resources/bookings/exam/exam_download.py @@ -12,12 +12,11 @@ See the License for the specific language governing permissions and limitations under the License.''' -from flask import Response +from flask import send_file from flask_restx import Resource import io import logging import urllib -from werkzeug.wsgi import FileWrapper from sqlalchemy import exc from app.models.theq import CSR from app.models.bookings import Exam @@ -50,15 +49,12 @@ def get(self, exam_id): req = urllib.request.Request(package_url) response = urllib.request.urlopen(req).read() exam_file = io.BytesIO(response) - file_wrapper = FileWrapper(exam_file) - - return Response(file_wrapper, - mimetype="application/pdf", - direct_passthrough=True, - headers={ - "Content-Disposition": 'attachment; filename="%s.csv"' % exam.exam_id, - "Content-Type": "application/pdf" - }) + return send_file( + exam_file, + mimetype="application/pdf", + as_attachment=True, + download_name=f"{exam.exam_id}.pdf" + ) else: return {'message': 'Package not yet generated', 'status': job['jobStatus']}, 400 diff --git a/api/app/resources/theq/login.py b/api/app/resources/theq/login.py index 6284c038f..86e0973a5 100644 --- a/api/app/resources/theq/login.py +++ b/api/app/resources/theq/login.py @@ -33,14 +33,6 @@ def get(self): if username != '': csr = CSR.find_by_username(username) if csr: - if csr.deleted is None: - csr.is_active = True - else: - csr.is_active = False - - csr.is_authenticated = False - csr.is_anonymous = False - login_user(csr) if application.config['USE_HTTPS']: return redirect(url_for(admin_index_const, diff --git a/api/qsystem.py b/api/qsystem.py index 72f594295..89c0783d6 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -72,7 +72,7 @@ def time_string(): logging.info(" --> ping_timeout_seconds: " + str(ping_timeout_seconds)) logging.info(" --> ping_interval_seconds: " + str(ping_interval_seconds)) -cache = Cache(config={'CACHE_TYPE': 'simple', 'CACHE_DEFAULT_TIMEOUT': application.config['CACHE_DEFAULT_TIMEOUT']}) +cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': application.config['CACHE_DEFAULT_TIMEOUT']}) cache.init_app(application) ma = Marshmallow(application) diff --git a/api/requirements.txt b/api/requirements.txt index df7abe601..49cee13a3 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,4 +1,4 @@ -alembic==1.5.2 +alembic==1.13.3 amqp==5.2.0 aniso8601==9.0.1 astroid==4.0.4 @@ -10,21 +10,21 @@ Brotli==1.1.0 cachelib==0.13.0 certifi==2024.7.4 charset-normalizer==3.3.2 -click==7.1.2 +click==8.1.7 decorator==5.0.9 dill==0.4.1 ecdsa==0.19.0 filelock==3.0.12 -Flask==1.1.4 -Flask-Admin==1.5.8 -Flask-Caching==1.11.1 -Flask-Compress==1.9.0 +Flask==2.3.3 +Flask-Admin==1.6.1 +Flask-Caching==2.3.1 +Flask-Compress==1.17 Flask-Cors==5.0.0 flask-jwt-oidc==0.3.0 -Flask-Login==0.5.0 +Flask-Login==0.6.3 flask-marshmallow==0.14.0 -Flask-Migrate==2.7.0 -flask-restx==0.5.1 +Flask-Migrate==3.1.0 +flask-restx==1.3.0 Flask-SocketIO==5.1.0 Flask-SQLAlchemy==2.5.1 gevent==24.2.1 @@ -37,14 +37,14 @@ importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 isort==8.0.1 -itsdangerous==1.1.0 -Jinja2==2.11.3 +itsdangerous==2.2.0 +Jinja2==3.1.6 jsonschema==4.22.0 jsonschema-specifications==2023.12.1 kombu==5.1.0 lazy-object-proxy==1.10.0 Mako==1.3.3 -MarkupSafe==2.0.1 +MarkupSafe==2.1.5 marshmallow==3.21.2 marshmallow-sqlalchemy==0.26.1 mccabe==0.6.1 @@ -86,7 +86,7 @@ tomlkit==0.14.0 typing_extensions==4.15.0 urllib3==1.26.19 vine==5.1.0 -Werkzeug==1.0.1 +Werkzeug==2.3.8 wrapt==1.12.1 wsproto==1.2.0 WTForms==3.0.0 From fc8777749f8f95fcafbb8a26a371d495dfac3f80 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 13:42:00 -0700 Subject: [PATCH 11/81] Refactor for SQLAlchemy 2.x - Updated database access methods from `query.get()` to `db.session.get()` for consistency and improved performance in `appointment_post.py`, `appointment_put.py`, `exam_list.py`, `citizen_detail.py`, `citizen_generic_invite.py`, `citizen_list.py`, `health.py`, `service_requests_detail.py`, `service_requests_list.py`, `services.py`, and `snowplow.py`. - Refactored citizen state retrieval logic into dedicated functions in `citizen_detail.py`, `citizen_list.py`, and `service_requests_list.py` to enhance readability and maintainability. - Improved error handling in `citizen_detail.py` and `citizen_generic_invite.py` by using `get_json(silent=True)` to avoid exceptions on empty requests. - Updated SQLAlchemy and Flask-SQLAlchemy versions - Added new smoke test scripts for SQLAlchemy to validate database interactions and ensure application stability. - Refactored application initialization in `manage.py` and `qsystem.py` to streamline the setup process and improve logging. - Removed deprecated code and comments to clean up the codebase and enhance clarity. --- api/app/admin/base.py | 23 ++ api/app/admin/csr.py | 6 +- api/app/admin/invigilator.py | 10 +- api/app/admin/office.py | 28 +- api/app/admin/room.py | 10 +- api/app/admin/timeslot.py | 10 +- api/app/models/bookings/appointments.py | 108 +++++++- api/app/models/bookings/base.py | 2 +- api/app/models/bookings/exam_type.py | 2 +- api/app/models/bookings/room.py | 2 +- api/app/models/theq/base.py | 2 +- api/app/models/theq/citizen.py | 2 +- api/app/models/theq/csr.py | 2 +- api/app/models/theq/office.py | 14 +- api/app/models/theq/period.py | 2 +- api/app/models/theq/public_user.py | 2 +- api/app/models/theq/role.py | 2 +- api/app/models/theq/service.py | 2 +- api/app/models/theq/service_req.py | 10 +- api/app/models/theq/time_slot.py | 2 +- .../appointment/appointment_availability.py | 2 +- .../appointment/appointment_draft_post.py | 2 +- .../bookings/appointment/appointment_post.py | 2 +- .../bookings/appointment/appointment_put.py | 2 +- api/app/resources/bookings/exam/exam_list.py | 6 +- .../resources/theq/citizen/citizen_detail.py | 25 +- .../theq/citizen/citizen_generic_invite.py | 10 +- .../resources/theq/citizen/citizen_list.py | 22 +- api/app/resources/theq/health.py | 2 +- .../resources/theq/service_requests_detail.py | 6 +- .../resources/theq/service_requests_list.py | 27 +- api/app/resources/theq/services.py | 6 +- api/app/resources/theq/smartboard.py | 17 +- api/app/tests/conftest.py | 157 ++++++++++++ api/app/tests/test.py | 40 --- api/app/tests/test_sqlalchemy_smoke.py | 81 ++++++ api/app/utilities/snowplow.py | 14 +- api/config.py | 11 +- api/manage.py | 2 - api/qsystem.py | 239 +++++++++--------- api/requirements.txt | 17 +- api/scripts/run_sqlalchemy_smoke_tests.sh | 8 + api/scripts/run_sqlalchemy_warn20_tests.sh | 11 + api/setup.cfg | 5 +- 44 files changed, 650 insertions(+), 305 deletions(-) create mode 100644 api/app/tests/conftest.py delete mode 100644 api/app/tests/test.py create mode 100644 api/app/tests/test_sqlalchemy_smoke.py create mode 100755 api/scripts/run_sqlalchemy_smoke_tests.sh create mode 100755 api/scripts/run_sqlalchemy_warn20_tests.sh diff --git a/api/app/admin/base.py b/api/app/admin/base.py index 97ebe6b6f..4caaa879a 100644 --- a/api/app/admin/base.py +++ b/api/app/admin/base.py @@ -12,7 +12,9 @@ See the License for the specific language governing permissions and limitations under the License.''' +from flask import has_request_context from flask_admin.contrib.sqla import ModelView +from flask_login import current_user from qsystem import application @@ -22,3 +24,24 @@ class Base(ModelView): def get_url(self, endpoint, **kwargs): new_kwargs = dict(kwargs, _external=True, _scheme=application.config['PREFERRED_URL_SCHEME']) return super(ModelView, self).get_url(endpoint, **new_kwargs) + + def get_current_user(self): + if not has_request_context(): + return None + + try: + if not current_user.is_authenticated: + return None + except Exception: + return None + + return current_user + + def get_current_role_code(self): + user = self.get_current_user() + role = getattr(user, 'role', None) + return getattr(role, 'role_code', None) + + def get_current_office_id(self): + user = self.get_current_user() + return getattr(user, 'office_id', None) diff --git a/api/app/admin/csr.py b/api/app/admin/csr.py index 10fbe1176..ec04e32e4 100644 --- a/api/app/admin/csr.py +++ b/api/app/admin/csr.py @@ -31,14 +31,14 @@ class CSRConfig(Base): roles_allowed = ['GA', 'HELPDESK', 'SUPPORT'] def is_accessible(self): - return current_user.is_authenticated and current_user.role.role_code in self.roles_allowed + return self.get_current_role_code() in self.roles_allowed create_modal = False edit_modal = False @property def can_create(self): - return current_user.role.role_code != 'GA' + return self.get_current_role_code() != 'GA' can_delete = False # Defining String constants to appease SonarQube @@ -125,7 +125,7 @@ def validate_model(self): model = self.get_one(identifier) allowed_ga_edit_roles = ['GA', 'CSR'] - if model and current_user.role.role_code == 'GA' and model.role.role_code not in allowed_ga_edit_roles: + if model and self.get_current_role_code() == 'GA' and model.role.role_code not in allowed_ga_edit_roles: flash(gettext('You are not allowed to edit a '+ model.role.role_code +' role.'), 'error') return False diff --git a/api/app/admin/invigilator.py b/api/app/admin/invigilator.py index f59bc5cad..656a6eb9f 100644 --- a/api/app/admin/invigilator.py +++ b/api/app/admin/invigilator.py @@ -26,13 +26,15 @@ class InvigilatorConfig(Base): office_name_const = 'office.office_name' def is_accessible(self): - return current_user.is_authenticated and current_user.role.role_code in self.roles_allowed + return self.get_current_role_code() in self.roles_allowed def get_query(self): - if current_user.role.role_code == 'SUPPORT': + role_code = self.get_current_role_code() + if role_code == 'GA': + return self.session.query(self.model).filter_by(office_id=self.get_current_office_id()) + if role_code == 'SUPPORT' or role_code is None: return self.session.query(self.model) - elif current_user.role.role_code == 'GA': - return self.session.query(self.model).filter_by(office_id=current_user.office_id) + return self.session.query(self.model) create_modal = False edit_modal = False diff --git a/api/app/admin/office.py b/api/app/admin/office.py index 620033a00..615022ff4 100644 --- a/api/app/admin/office.py +++ b/api/app/admin/office.py @@ -17,7 +17,7 @@ from app.models.theq import Office, Service, Counter from .base import Base from flask_login import current_user -from flask import flash, url_for, has_app_context +from flask import flash, url_for from flask_admin.babel import gettext from qsystem import db from sqlalchemy import and_ @@ -34,15 +34,15 @@ class OfficeConfig(Base): roles_allowed = ['SUPPORT', 'GA'] def is_accessible(self): - return current_user.is_authenticated and current_user.role.role_code in self.roles_allowed + return self.get_current_role_code() in self.roles_allowed @property def can_create(self): - return current_user.role.role_code != 'GA' + return self.get_current_role_code() != 'GA' @property def column_list(self): - if has_app_context() and current_user.role.role_code == 'SUPPORT': + if self.get_current_role_code() != 'GA': return self.column_list_support return self.column_list_GA @@ -55,10 +55,12 @@ def _list_columns(self, value): pass # This is empty for some reason. def get_query(self): - if current_user.role.role_code == 'SUPPORT': + role_code = self.get_current_role_code() + if role_code == 'GA': + return self.session.query(self.model).filter_by(office_id=self.get_current_office_id()) + if role_code == 'SUPPORT' or role_code is None: return self.session.query(self.model) - elif current_user.role.role_code == 'GA': - return self.session.query(self.model).filter_by(office_id=current_user.office_id) + return self.session.query(self.model) create_modal = False edit_modal = False @@ -323,7 +325,11 @@ def get_query(self): } def on_model_change(self, form, model, is_created): - csr = CSR.find_by_username(current_user.username) + user = self.get_current_user() + if user is None: + return + + csr = CSR.find_by_username(user.username) socketio.emit('clear_csr_cache', { "id": csr.csr_id}) socketio.emit('csr_update', {"csr_id": csr.csr_id, "receptionist_ind": csr.receptionist_ind}, @@ -331,12 +337,14 @@ def on_model_change(self, form, model, is_created): socketio.emit('digital_signage_msg_update') def render(self, template, **kwargs): - if current_user.role.role_code == 'SUPPORT': + role_code = self.get_current_role_code() + + if role_code == 'SUPPORT': if template == 'admin/model/edit.html': template = 'office/office_edit.html' elif template == 'admin/model/create.html': template = 'office/office_create.html' - elif current_user.role.role_code == 'GA': + elif role_code == 'GA': if template == 'admin/model/edit.html': template = 'office/officega_edit.html' elif template == 'admin/model/create.html': diff --git a/api/app/admin/room.py b/api/app/admin/room.py index 8120f9b58..dd471e71f 100644 --- a/api/app/admin/room.py +++ b/api/app/admin/room.py @@ -27,13 +27,15 @@ class RoomConfig(Base): roles_allowed = ['SUPPORT', 'GA'] def is_accessible(self): - return current_user.is_authenticated and current_user.role.role_code in self.roles_allowed + return self.get_current_role_code() in self.roles_allowed def get_query(self): - if current_user.role.role_code == 'SUPPORT': + role_code = self.get_current_role_code() + if role_code == 'GA': + return self.session.query(self.model).filter_by(office_id=self.get_current_office_id()) + if role_code == 'SUPPORT' or role_code is None: return self.session.query(self.model) - elif current_user.role.role_code == 'GA': - return self.session.query(self.model).filter_by(office_id=current_user.office_id) + return self.session.query(self.model) def on_model_change(self, form, model, is_created): diff --git a/api/app/admin/timeslot.py b/api/app/admin/timeslot.py index 8dccbde09..7a2bc017e 100644 --- a/api/app/admin/timeslot.py +++ b/api/app/admin/timeslot.py @@ -69,13 +69,15 @@ class TimeslotConfig(Base): roles_allowed = ['SUPPORT', 'GA'] def is_accessible(self): - return current_user.is_authenticated and current_user.role.role_code in self.roles_allowed + return self.get_current_role_code() in self.roles_allowed def get_query(self): - if current_user.role.role_code == 'SUPPORT': + role_code = self.get_current_role_code() + if role_code == 'GA': + return self.session.query(self.model).filter_by(office_id=self.get_current_office_id()) + if role_code == 'SUPPORT' or role_code is None: return self.session.query(self.model) - elif current_user.role.role_code == 'GA': - return self.session.query(self.model).filter_by(office_id=current_user.office_id) + return self.session.query(self.model) create_modal = False edit_modal = False diff --git a/api/app/models/bookings/appointments.py b/api/app/models/bookings/appointments.py index f98bfbf80..37aa843b4 100644 --- a/api/app/models/bookings/appointments.py +++ b/api/app/models/bookings/appointments.py @@ -15,20 +15,16 @@ from app.models.bookings import Base from qsystem import db from sqlalchemy_utc import UtcDateTime, utcnow -from sqlalchemy import func, or_, and_ +from sqlalchemy import event, func, or_, and_, text from datetime import datetime, timedelta, timezone from dateutil.parser import parse from dateutil import tz from app.utilities.date_util import current_pacific_time -from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.orm import declared_attr from flask import g class Appointment(Base): - __versioned__ = { - 'exclude': [] - } - appointment_id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False) office_id = db.Column(db.Integer, db.ForeignKey("office.office_id"), nullable=False) service_id = db.Column(db.Integer, db.ForeignKey("service.service_id"), nullable=True) @@ -175,5 +171,103 @@ def delete_expired_drafts(cls): draft_ids = [appointment.appointment_id for appointment in drafts] Appointment.delete_appointments(draft_ids) return draft_ids - + +def _record_appointment_version(connection, appointment, operation_type): + transaction_id = connection.execute( + text( + "INSERT INTO transaction (issued_at, remote_addr) " + "VALUES (:issued_at, :remote_addr) RETURNING id" + ), + {"issued_at": datetime.utcnow(), "remote_addr": None}, + ).scalar() + + connection.execute( + text( + """ + INSERT INTO appointment_version ( + appointment_id, + office_id, + service_id, + citizen_id, + start_time, + end_time, + checked_in_time, + comments, + citizen_name, + contact_information, + blackout_flag, + recurring_uuid, + online_flag, + is_draft, + created_at, + stat_flag, + updated_at, + updated_by, + transaction_id, + end_transaction_id, + operation_type + ) VALUES ( + :appointment_id, + :office_id, + :service_id, + :citizen_id, + :start_time, + :end_time, + :checked_in_time, + :comments, + :citizen_name, + :contact_information, + :blackout_flag, + :recurring_uuid, + :online_flag, + :is_draft, + :created_at, + :stat_flag, + :updated_at, + :updated_by, + :transaction_id, + :end_transaction_id, + :operation_type + ) + """ + ), + { + "appointment_id": appointment.appointment_id, + "office_id": appointment.office_id, + "service_id": appointment.service_id, + "citizen_id": appointment.citizen_id, + "start_time": appointment.start_time, + "end_time": appointment.end_time, + "checked_in_time": appointment.checked_in_time, + "comments": appointment.comments, + "citizen_name": appointment.citizen_name, + "contact_information": appointment.contact_information, + "blackout_flag": appointment.blackout_flag, + "recurring_uuid": appointment.recurring_uuid, + "online_flag": appointment.online_flag, + "is_draft": appointment.is_draft, + "created_at": appointment.created_at, + "stat_flag": appointment.stat_flag, + "updated_at": appointment.updated_at, + "updated_by": appointment.updated_by, + "transaction_id": transaction_id, + "end_transaction_id": None, + "operation_type": operation_type, + }, + ) + + +@event.listens_for(Appointment, "after_insert") +def _record_appointment_insert(mapper, connection, target): # pragma: no cover - SQLAlchemy hook + _record_appointment_version(connection, target, 0) + + +@event.listens_for(Appointment, "after_update") +def _record_appointment_update(mapper, connection, target): # pragma: no cover - SQLAlchemy hook + _record_appointment_version(connection, target, 1) + + +@event.listens_for(Appointment, "after_delete") +def _record_appointment_delete(mapper, connection, target): # pragma: no cover - SQLAlchemy hook + _record_appointment_version(connection, target, 2) diff --git a/api/app/models/bookings/base.py b/api/app/models/bookings/base.py index de3cfaa6b..af17f625d 100644 --- a/api/app/models/bookings/base.py +++ b/api/app/models/bookings/base.py @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License.''' -from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.orm import declared_attr from qsystem import db diff --git a/api/app/models/bookings/exam_type.py b/api/app/models/bookings/exam_type.py index 8574d4473..e9c725093 100644 --- a/api/app/models/bookings/exam_type.py +++ b/api/app/models/bookings/exam_type.py @@ -30,7 +30,7 @@ class ExamType(Base): deleted = db.Column(db.DateTime, nullable=True) # changed lazy=false to lazy=raise - exam = db.relationship("Exam", lazy='raise') + exam = db.relationship("Exam", lazy='raise', overlaps='exam_type') # changed lazy4-false to no lazy option #exam = db.relationship("Exam", lazy=False) diff --git a/api/app/models/bookings/room.py b/api/app/models/bookings/room.py index 2a77f7846..00b20c50d 100644 --- a/api/app/models/bookings/room.py +++ b/api/app/models/bookings/room.py @@ -25,7 +25,7 @@ class Room(Base): color = db.Column(db.String(25), nullable=False) deleted = db.Column(db.DateTime, nullable=True) - booking = db.relationship("Booking") + booking = db.relationship("Booking", overlaps='room') office = db.relationship("Office", lazy='joined') def __repr__(self): diff --git a/api/app/models/theq/base.py b/api/app/models/theq/base.py index 3c6840949..d58d9350d 100644 --- a/api/app/models/theq/base.py +++ b/api/app/models/theq/base.py @@ -13,7 +13,7 @@ limitations under the License.''' from qsystem import db -from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.orm import declared_attr class Base(db.Model, object): diff --git a/api/app/models/theq/citizen.py b/api/app/models/theq/citizen.py index 987a96e4a..2add822a7 100644 --- a/api/app/models/theq/citizen.py +++ b/api/app/models/theq/citizen.py @@ -87,4 +87,4 @@ def find_citizen_by_user_id(cls, user_id, office_id): @classmethod def find_citizen_by_id(cls, citizen_id): """Find citizen record by user id.""" - return cls.query.get(citizen_id) + return db.session.get(cls, citizen_id) diff --git a/api/app/models/theq/csr.py b/api/app/models/theq/csr.py index bdfb21c47..cf2e4bb25 100644 --- a/api/app/models/theq/csr.py +++ b/api/app/models/theq/csr.py @@ -39,7 +39,7 @@ class CSR(UserMixin, Base): office = db.relationship("Office", lazy='joined') counter = db.relationship("Counter", lazy='joined') periods = db.relationship("Period", primaryjoin="and_(CSR.csr_id==Period.csr_id,Period.time_end.is_(None))", - order_by='desc(Period.time_start)') + order_by='desc(Period.time_start)', overlaps='csr') format_string = 'csr_detail_%s' diff --git a/api/app/models/theq/office.py b/api/app/models/theq/office.py index 4ac1218a1..95239e659 100644 --- a/api/app/models/theq/office.py +++ b/api/app/models/theq/office.py @@ -90,18 +90,18 @@ class Office(Base): soonest_appointment = db.Column(db.Integer, default=0) counters = db.relationship("Counter", secondary='office_counter') - services = db.relationship("Service", secondary='office_service') + services = db.relationship("Service", secondary='office_service', overlaps='offices') quick_list = db.relationship("Service", secondary='office_quick_list') back_office_list = db.relationship("Service", secondary='office_back_office_list') - csrs = db.relationship('CSR') - citizens = db.relationship('Citizen', backref='office_citizens') - timeslots = db.relationship('TimeSlot') + csrs = db.relationship('CSR', overlaps='office') + citizens = db.relationship('Citizen', backref=db.backref('office_citizens', overlaps='office'), overlaps='office') + timeslots = db.relationship('TimeSlot', overlaps='office') sb = db.relationship('SmartBoard') timezone = db.relationship('Timezone') - exams = db.relationship("Exam") - rooms = db.relationship('Room') + exams = db.relationship("Exam", overlaps='office') + rooms = db.relationship('Room', overlaps='office') # for walk-in notifications check_in_notification = db.Column(db.Integer) @@ -131,7 +131,7 @@ def find_by_id(cls, office_id: int): key = Office.format_string % office_id office = cache.get(key) if not office: - office = cls.query.get(office_id) + office = db.session.get(cls, office_id) office.timeslots office.timezone return office diff --git a/api/app/models/theq/period.py b/api/app/models/theq/period.py index 8c81240b9..7e18d7ca8 100644 --- a/api/app/models/theq/period.py +++ b/api/app/models/theq/period.py @@ -26,7 +26,7 @@ class Period(Base): time_start = db.Column(db.DateTime, nullable=False) time_end = db.Column(db.DateTime, nullable=True) - csr = db.relationship("CSR", lazy='joined') + csr = db.relationship("CSR", lazy='joined', overlaps='periods') ps = db.relationship("PeriodState", lazy='joined') sr = db.relationship("ServiceReq", lazy='joined') diff --git a/api/app/models/theq/public_user.py b/api/app/models/theq/public_user.py index 1f6be6f3a..be9647020 100644 --- a/api/app/models/theq/public_user.py +++ b/api/app/models/theq/public_user.py @@ -46,7 +46,7 @@ def find_by_username(cls, username): @classmethod def find_by_user_id(cls, user_id): """Find User records by user_id.""" - user = cls.query.get(user_id) + user = db.session.get(cls, user_id) return user @classmethod diff --git a/api/app/models/theq/role.py b/api/app/models/theq/role.py index c71effb89..b9c60fa9b 100644 --- a/api/app/models/theq/role.py +++ b/api/app/models/theq/role.py @@ -26,7 +26,7 @@ class Role(Base): role_code = db.Column(db.String(100)) role_desc = db.Column(db.String(1000)) - roles = db.relationship('CSR', lazy=False) + roles = db.relationship('CSR', lazy=False, overlaps='role') def __repr__(self): return self.role_code diff --git a/api/app/models/theq/service.py b/api/app/models/theq/service.py index 759fec83d..c3664f0f0 100644 --- a/api/app/models/theq/service.py +++ b/api/app/models/theq/service.py @@ -49,7 +49,7 @@ class Service(Base): email_paragraph = db.Column(db.String(2000), nullable=True) css_colour = db.Column(db.String(50), nullable=True) - offices = db.relationship("Office", secondary='office_service') + offices = db.relationship("Office", secondary='office_service', overlaps='services') parent = db.relationship("Service", remote_side=[service_id]) def __repr__(self): diff --git a/api/app/models/theq/service_req.py b/api/app/models/theq/service_req.py index 2046c153c..8a1ad8fbf 100644 --- a/api/app/models/theq/service_req.py +++ b/api/app/models/theq/service_req.py @@ -29,9 +29,15 @@ class ServiceReq(Base): sr_number = db.Column(db.Integer, default=1, nullable=False) channel = db.relationship('Channel') - periods = db.relationship('Period', backref=db.backref("request_periods", lazy=False), lazy='joined', order_by='Period.period_id') + periods = db.relationship( + 'Period', + backref=db.backref("request_periods", lazy=False, overlaps='sr'), + lazy='joined', + order_by='Period.period_id', + overlaps='sr' + ) sr_state = db.relationship('SRState', lazy='joined') - citizen = db.relationship('Citizen') + citizen = db.relationship('Citizen', overlaps='service_reqs') service = db.relationship('Service', lazy='joined') # Defining String constants to appease SonarQube diff --git a/api/app/models/theq/time_slot.py b/api/app/models/theq/time_slot.py index f9209e1e6..27bf74f08 100644 --- a/api/app/models/theq/time_slot.py +++ b/api/app/models/theq/time_slot.py @@ -27,7 +27,7 @@ class TimeSlot(Base): day_of_week = db.Column(postgresql.ARRAY(String), nullable=False) no_of_slots = db.Column(db.Integer, nullable=False) - office = db.relationship("Office", lazy='joined') + office = db.relationship("Office", lazy='joined', overlaps='timeslots') format_string = 'time_slot_%s' diff --git a/api/app/resources/bookings/appointment/appointment_availability.py b/api/app/resources/bookings/appointment/appointment_availability.py index c60de79ba..2b66683e5 100644 --- a/api/app/resources/bookings/appointment/appointment_availability.py +++ b/api/app/resources/bookings/appointment/appointment_availability.py @@ -47,7 +47,7 @@ def get(self, office_id: int): service = None service_id = request.args.get('service_id') if (service_id): - service = Service.query.get(int(service_id)) + service = db.session.get(Service, int(service_id)) return AvailabilityService.get_available_slots(office=office, days=days, service=service) diff --git a/api/app/resources/bookings/appointment/appointment_draft_post.py b/api/app/resources/bookings/appointment/appointment_draft_post.py index 4e70127f4..45f6ebe38 100644 --- a/api/app/resources/bookings/appointment/appointment_draft_post.py +++ b/api/app/resources/bookings/appointment/appointment_draft_post.py @@ -44,7 +44,7 @@ def post(self): start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) office = Office.find_by_id(office_id) - service = Service.query.get(int(service_id)) if service_id else None + service = db.session.get(Service, int(service_id)) if service_id else None # end_time can be null for CSRs when they click; whereas citizens know end-time. if not end_time: diff --git a/api/app/resources/bookings/appointment/appointment_post.py b/api/app/resources/bookings/appointment/appointment_post.py index 3e296081d..1ffa82732 100644 --- a/api/app/resources/bookings/appointment/appointment_post.py +++ b/api/app/resources/bookings/appointment/appointment_post.py @@ -87,7 +87,7 @@ def post(self): citizen.citizen_name = user.display_name office = Office.find_by_id(office_id) - service = Service.query.get(int(service_id)) + service = db.session.get(Service, int(service_id)) # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id(office_id=office_id, diff --git a/api/app/resources/bookings/appointment/appointment_put.py b/api/app/resources/bookings/appointment/appointment_put.py index f3553d396..5512cb2ee 100644 --- a/api/app/resources/bookings/appointment/appointment_put.py +++ b/api/app/resources/bookings/appointment/appointment_put.py @@ -70,7 +70,7 @@ def put(self, id): start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) service_id = json_data.get('service_id') - service = Service.query.get(int(service_id)) + service = db.session.get(Service, int(service_id)) if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time, service=service): return {"code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to scheduling conflict. Please pick another time."}, 400 diff --git a/api/app/resources/bookings/exam/exam_list.py b/api/app/resources/bookings/exam/exam_list.py index dd5cc2229..3e48dc7a7 100644 --- a/api/app/resources/bookings/exam/exam_list.py +++ b/api/app/resources/bookings/exam/exam_list.py @@ -17,7 +17,7 @@ from flask_restx import Resource from sqlalchemy import exc, or_, desc from app.models.bookings import Exam -from app.models.theq import CSR +from app.models.theq import CSR, Office from app.schemas.bookings import ExamSchema from qsystem import api from datetime import datetime, timedelta @@ -42,8 +42,8 @@ def get(self): exams = Exam.query.filter(Exam.deleted_date.is_(None)) \ .filter(or_(Exam.exam_returned_date.is_(None), Exam.exam_returned_date > ninety_day_filter)) \ - .join(Exam.office, aliased=True) \ - .filter_by(office_number=request.args.get("office_number")) \ + .join(Exam.office) \ + .filter(Office.office_number == request.args.get("office_number")) \ .order_by(desc(Exam.exam_id)) else: exams = Exam.query.filter(Exam.deleted_date.is_(None)) \ diff --git a/api/app/resources/theq/citizen/citizen_detail.py b/api/app/resources/theq/citizen/citizen_detail.py index 214d51fef..60dbbc16b 100644 --- a/api/app/resources/theq/citizen/citizen_detail.py +++ b/api/app/resources/theq/citizen/citizen_detail.py @@ -50,13 +50,10 @@ def get(self, id): @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def put(self, id): - json_data = request.get_json() + json_data = request.get_json(silent=True) or {} if 'counter_id' not in json_data: - json_data['counter_id'] = counter_id - - if not json_data: - return {'message': 'No input data received for updating citizen'}, 400 + json_data['counter_id'] = get_counter_id() csr = CSR.find_by_username(get_username()) citizen = Citizen.query.filter_by(citizen_id=id).first() @@ -108,13 +105,11 @@ def put(self, id): return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 200 - -try: - counter = Counter.query.filter(Counter.counter_name=="Counter")[0] - counter_id = counter.counter_id -# NOTE!! There should ONLY be an exception when first building the database -# from a python3 manage.py db upgrade command. -except: - counter_id = 1 - logging.exception("==> In citizen_detail.py") - logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") +def get_counter_id(): + try: + counter = Counter.query.filter(Counter.counter_name == "Counter")[0] + return counter.counter_id + except Exception: + logging.exception("==> In citizen_detail.py") + logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") + return 1 diff --git a/api/app/resources/theq/citizen/citizen_generic_invite.py b/api/app/resources/theq/citizen/citizen_generic_invite.py index 57ffd8f56..3081458cd 100644 --- a/api/app/resources/theq/citizen/citizen_generic_invite.py +++ b/api/app/resources/theq/citizen/citizen_generic_invite.py @@ -125,7 +125,7 @@ def post(self): lock = FileLock("lock/invite_citizen_{}.lock".format(csr.office_id)) with lock: - active_citizen_state = citizen_state + active_citizen_state = find_active() waiting_period_state = find_wait() citizen = None @@ -170,11 +170,3 @@ def post(self): return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 200 - -try: - citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() - active_id = citizen_state.cs_id -except: - active_id = 1 - logging.exception("==> In citizen_generic_invite.py") - logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") diff --git a/api/app/resources/theq/citizen/citizen_list.py b/api/app/resources/theq/citizen/citizen_list.py index 217283000..2ff8c5773 100644 --- a/api/app/resources/theq/citizen/citizen_list.py +++ b/api/app/resources/theq/citizen/citizen_list.py @@ -45,7 +45,7 @@ def get(self): citizens = Citizen.query \ .options(joinedload(Citizen.service_reqs, innerjoin=True).joinedload(ServiceReq.periods).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),raiseload(Citizen.office),raiseload(Citizen.counter),raiseload(Citizen.user)) \ - .filter_by(office_id=csr.office_id, cs_id=active_id) \ + .filter_by(office_id=csr.office_id, cs_id=get_active_citizen_state_id()) \ .order_by(Citizen.priority) result = self.citizens_schema.dump(citizens) @@ -69,7 +69,7 @@ def post(self, citizens_waiting): has_role([Role.internal_user.value], g.jwt_oidc_token_info['realm_access']['roles'], username, "CitizenList POST /citizens/") - json_data = request.get_json() + json_data = request.get_json(silent=True) csr = CSR.find_by_username(username) if not csr: @@ -89,7 +89,7 @@ def post(self, citizens_waiting): logging.exception(err) return {"message": err.messages}, 422 - citizen.cs_id = active_id + citizen.cs_id = get_active_citizen_state_id() citizen.service_count = 1 db.session.add(citizen) db.session.commit() @@ -101,11 +101,11 @@ def post(self, citizens_waiting): return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 201 -try: - key = get_key() - citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() - active_id = citizen_state.cs_id -except: - active_id = 1 - logging.exception("==> In citizen_list.py") - logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") +def get_active_citizen_state_id(): + try: + citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() + return citizen_state.cs_id + except Exception: + logging.exception("==> In citizen_list.py") + logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") + return 1 diff --git a/api/app/resources/theq/health.py b/api/app/resources/theq/health.py index 1dd8bf280..f6090fb41 100644 --- a/api/app/resources/theq/health.py +++ b/api/app/resources/theq/health.py @@ -24,7 +24,7 @@ class Healthz(Resource): @staticmethod def get(): try: - db.engine.execute(sql) + db.session.execute(sql) except exc.SQLAlchemyError: return {"message": "api is down"}, 500 diff --git a/api/app/resources/theq/service_requests_detail.py b/api/app/resources/theq/service_requests_detail.py index afdebdcbc..e3a7eb36c 100644 --- a/api/app/resources/theq/service_requests_detail.py +++ b/api/app/resources/theq/service_requests_detail.py @@ -43,7 +43,7 @@ def put(self, id): csr = CSR.find_by_username(get_username()) service_request = ServiceReq.query.filter_by(sr_id=id) \ - .join(ServiceReq.citizen, aliased=True).first_or_404() + .join(ServiceReq.citizen).first_or_404() try: service_request = self.service_request_schema.load(json_data, instance=service_request, partial=True) @@ -77,8 +77,8 @@ def post(self, id): csr = CSR.find_by_username(get_username()) service_request = ServiceReq.query.filter_by(sr_id=id) \ - .join(ServiceReq.citizen, aliased=True) \ - .filter_by(office_id=csr.office_id).first_or_404() + .join(ServiceReq.citizen) \ + .filter(Citizen.office_id == csr.office_id).first_or_404() active_service_state = SRState.get_state_by_name("Active") complete_service_state = SRState.get_state_by_name("Complete") diff --git a/api/app/resources/theq/service_requests_list.py b/api/app/resources/theq/service_requests_list.py index b364f4dec..9836d683a 100644 --- a/api/app/resources/theq/service_requests_list.py +++ b/api/app/resources/theq/service_requests_list.py @@ -67,7 +67,7 @@ def get_service(service_request, json_data, csr): service = None try: - service = Service.query.get(service_request.service_id) + service = db.session.get(Service, service_request.service_id) except: logging.exception("==> An exception getting service info") logging.exception(csr_const + csr.username) @@ -153,11 +153,11 @@ def post(self): offset_start_time = citizen.start_time - timedelta(hours=11) service_count = ServiceReq.query \ - .join(ServiceReq.citizen, aliased=True) \ + .join(ServiceReq.citizen) \ .filter(Citizen.start_time >= offset_start_time.strftime("%Y-%m-%d")) \ - .filter_by(office_id=csr.office_id) \ - .join(ServiceReq.service, aliased=True) \ - .filter_by(prefix=service.prefix) \ + .filter(Citizen.office_id == csr.office_id) \ + .join(ServiceReq.service) \ + .filter(Service.prefix == service.prefix) \ .count() citizen.ticket_number = service.prefix + str(service_count) @@ -172,7 +172,7 @@ def post(self): ) service_request.periods.append(ticket_create_period) - citizen.cs_id = active_id + citizen.cs_id = get_active_citizen_state_id() # If first service, just choose it. If additional service, more work needed. if len(citizen.service_reqs) == 0: @@ -203,10 +203,11 @@ def post(self): return {'service_request': result, 'errors': self.service_request_schema.validate(service_request)}, 201 -try: - citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() - active_id = citizen_state.cs_id -except: - active_id = 1 - logging.exception("==> In service_requests_list.py") - logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") +def get_active_citizen_state_id(): + try: + citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() + return citizen_state.cs_id + except Exception: + logging.exception("==> In service_requests_list.py") + logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") + return 1 diff --git a/api/app/resources/theq/services.py b/api/app/resources/theq/services.py index 651197612..6978598a3 100644 --- a/api/app/resources/theq/services.py +++ b/api/app/resources/theq/services.py @@ -53,7 +53,7 @@ def top_reqs(is_back_office=True): # Get top requests for the office, and set the lists based on those. results = ServiceReq.query.options( - noload('*'), joinedload('service') + noload('*'), joinedload(ServiceReq.service) ).join( Citizen ).join( @@ -104,7 +104,7 @@ def top_reqs(is_back_office=True): quick_list = top_reqs(is_back_office=False) back_office_list = top_reqs(is_back_office=True) - office = Office.query.get(office_id) + office = db.session.get(Office, office_id) office.quick_list = quick_list office.back_office_list = back_office_list db.session.commit() @@ -141,7 +141,7 @@ def get(self): if request.args.get('office_id'): try: office_id = int(request.args['office_id']) - office = Office.query.get(office_id) + office = db.session.get(Office, office_id) services = sorted(office.services, key=cmp_to_key(self.sort_services)) filtered_services = [s for s in services if s.deleted is None] result = self.service_schema.dump(filtered_services) diff --git a/api/app/resources/theq/smartboard.py b/api/app/resources/theq/smartboard.py index 454013c13..06bbee4e2 100644 --- a/api/app/resources/theq/smartboard.py +++ b/api/app/resources/theq/smartboard.py @@ -43,7 +43,7 @@ def get(self): citizens = Citizen.query \ .options(joinedload(Citizen.service_reqs, innerjoin=True).options(joinedload(ServiceReq.periods).options(raiseload(Period.csr),raiseload(Period.sr))), raiseload(Citizen.cs),raiseload(Citizen.counter),raiseload(Citizen.user)) \ .filter_by(office_id=office.office_id) \ - .filter_by(cs_id=active_citizen_state) + .filter_by(cs_id=get_active_citizen_state_id()) for c in citizens: active_service_request = c.get_active_service_request() @@ -99,10 +99,11 @@ def get(self, id): logging.exception(exception) return {'message': 'API is down'}, 500 -try: - citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() - active_citizen_state = citizen_state.cs_id -except Exception as ex: - active_citizen_state = 1 - logging.exception("==> In smartboard.py") - logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") \ No newline at end of file +def get_active_citizen_state_id(): + try: + citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() + return citizen_state.cs_id + except Exception: + logging.exception("==> In smartboard.py") + logging.exception(" --> NOTE!! You should only see this if doing a 'python3 manage.py db upgrade'") + return 1 diff --git a/api/app/tests/conftest.py b/api/app/tests/conftest.py new file mode 100644 index 000000000..86013a83f --- /dev/null +++ b/api/app/tests/conftest.py @@ -0,0 +1,157 @@ +import importlib +import os +import sys +import uuid +from contextlib import closing + +import pytest + +try: + import psycopg2 + from psycopg2 import sql +except ImportError: # pragma: no cover - handled by session skip + psycopg2 = None + sql = None + + +def _db_settings(): + return { + "engine": os.getenv("TEST_DATABASE_ENGINE", "postgresql+psycopg2"), + "host": os.getenv("TEST_DATABASE_HOST", os.getenv("DATABASE_HOST", "127.0.0.1")), + "port": os.getenv("TEST_DATABASE_PORT", os.getenv("DATABASE_PORT", "5432")), + "user": os.getenv("TEST_DATABASE_USERNAME", os.getenv("DATABASE_USERNAME", "postgres")), + "password": os.getenv("TEST_DATABASE_PASSWORD", os.getenv("DATABASE_PASSWORD", "root")), + "admin_db": os.getenv("TEST_DATABASE_ADMIN_DB", "postgres"), + } + + +def _connect(database_name): + settings = _db_settings() + return psycopg2.connect( + dbname=database_name, + user=settings["user"], + password=settings["password"], + host=settings["host"], + port=settings["port"], + ) + + +@pytest.fixture(scope="session") +def postgres_database(): + if psycopg2 is None: + pytest.skip("psycopg2 is required for the SQLAlchemy smoke suite") + + database_name = f"qsystem_sqlalchemy_smoke_{uuid.uuid4().hex[:12]}" + settings = _db_settings() + + try: + with closing(_connect(settings["admin_db"])) as connection: + connection.autocommit = True + with connection.cursor() as cursor: + cursor.execute( + sql.SQL("CREATE DATABASE {}").format(sql.Identifier(database_name)) + ) + except Exception as exc: # pragma: no cover - depends on local services + pytest.skip(f"unable to create disposable Postgres database: {exc}") + + original_env = { + key: os.environ.get(key) + for key in ( + "FLASK_CONFIGURATION", + "DATABASE_ENGINE", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_USERNAME", + "DATABASE_PASSWORD", + "DATABASE_NAME", + ) + } + + os.environ.update( + { + "FLASK_CONFIGURATION": "localhost", + "DATABASE_ENGINE": settings["engine"], + "DATABASE_HOST": settings["host"], + "DATABASE_PORT": settings["port"], + "DATABASE_USERNAME": settings["user"], + "DATABASE_PASSWORD": settings["password"], + "DATABASE_NAME": database_name, + } + ) + + try: + yield { + "database_name": database_name, + "database_uri": ( + f"{settings['engine']}://{settings['user']}:{settings['password']}" + f"@{settings['host']}:{settings['port']}/{database_name}" + ), + } + finally: + for module_name in ("manage", "qsystem"): + module = sys.modules.get(module_name) + if module is not None and hasattr(module, "db"): + try: + module.db.session.remove() + if hasattr(module, "application"): + with module.application.app_context(): + module.db.engine.dispose() + except Exception: + pass + + for module_name in list(sys.modules): + if module_name == "app" or module_name.startswith("app.") or module_name in ("manage", "qsystem"): + sys.modules.pop(module_name, None) + + try: + with closing(_connect(settings["admin_db"])) as connection: + connection.autocommit = True + with connection.cursor() as cursor: + cursor.execute( + sql.SQL( + "SELECT pg_terminate_backend(pid) " + "FROM pg_stat_activity WHERE datname = %s AND pid <> pg_backend_pid()" + ), + (database_name,), + ) + cursor.execute( + sql.SQL("DROP DATABASE IF EXISTS {}").format(sql.Identifier(database_name)) + ) + finally: + for key, value in original_env.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + +@pytest.fixture(scope="session") +def app_module(postgres_database): + return importlib.import_module("manage") + + +@pytest.fixture(scope="session") +def app(app_module): + return app_module.application + + +@pytest.fixture(scope="session") +def db(app_module): + return app_module.db + + +@pytest.fixture(scope="session") +def cli_runner(app): + return app.test_cli_runner() + + +@pytest.fixture(scope="session") +def client(app): + return app.test_client() + + +@pytest.fixture(scope="session") +def migrated_database(cli_runner): + result = cli_runner.invoke(args=["db", "upgrade"]) + assert result.exit_code == 0, result.output + return result diff --git a/api/app/tests/test.py b/api/app/tests/test.py deleted file mode 100644 index cf747fc27..000000000 --- a/api/app/tests/test.py +++ /dev/null @@ -1,40 +0,0 @@ -import json -import os -import tempfile -import unittest - -from qsystem import db, application -from app.models import theq -from app.schemas.theq import ServiceReqSchema - - -class QSystemTestCase(unittest.TestCase): - - def setUp(self): - self.db_fd, application.config['DATABASE'] = tempfile.mkstemp() - application.testing = True - self.app = application.test_client() - - with application.app_context(): - db.init_app(application) - db.drop_all() - db.create_all() - - def test_create_edit_delete_note(self): - with application.app_context(): - service_request_schema = ServiceReqSchema() - - json_data = {'service_id': 12, 'citizen_id': '', 'quantity': 3, 'channel_id': 2} - - service_request = service_request_schema.load(json_data) - - # Confirm a marshmallow bug, returns a dict when invalid data passed in. - assert type(service_request) is dict - - - def tearDown(self): - os.close(self.db_fd) - os.unlink(application.config['DATABASE']) - -if __name__ == '__main__': - unittest.main() diff --git a/api/app/tests/test_sqlalchemy_smoke.py b/api/app/tests/test_sqlalchemy_smoke.py new file mode 100644 index 000000000..f6bffcad8 --- /dev/null +++ b/api/app/tests/test_sqlalchemy_smoke.py @@ -0,0 +1,81 @@ +from datetime import datetime, timedelta, timezone + +from sqlalchemy import text + + +def test_app_boots_with_disposable_postgres(app, postgres_database): + assert app.config["SQLALCHEMY_DATABASE_URI"] == postgres_database["database_uri"] + + with app.app_context(): + assert app.extensions["sqlalchemy"] + + +def test_db_current_command_runs(cli_runner): + result = cli_runner.invoke(args=["db", "current"]) + + assert result.exit_code == 0, result.output + + +def test_db_upgrade_command_runs(migrated_database): + assert migrated_database.exit_code == 0, migrated_database.output + + +def test_healthz_uses_database_connection(client, migrated_database): + response = client.get("/api/v1/healthz/") + + assert response.status_code == 200 + assert response.get_json() == {"message": "api is healthy"} + + +def test_appointment_crud_and_version_rows(app, db, migrated_database): + from app.models.bookings import Appointment + from app.models.theq.office import Office + from app.models.theq.smartboard import SmartBoard + from app.models.theq.timezone import Timezone + + with app.app_context(): + smartboard = SmartBoard(sb_type="callbyticket") + timezone_row = Timezone(timezone_name="Canada/Pacific") + db.session.add_all([smartboard, timezone_row]) + db.session.commit() + + office = Office( + office_name="SQLAlchemy Smoke Office", + office_number=9999, + sb_id=smartboard.sb_id, + exams_enabled_ind=0, + appointments_enabled_ind=1, + timezone_id=timezone_row.timezone_id, + ) + db.session.add(office) + db.session.commit() + + appointment = Appointment( + office_id=office.office_id, + start_time=datetime.now(timezone.utc), + end_time=datetime.now(timezone.utc) + timedelta(minutes=30), + citizen_name="Smoke Test Citizen", + contact_information="smoke@example.com", + ) + db.session.add(appointment) + db.session.commit() + + appointment.comments = "updated by smoke suite" + db.session.add(appointment) + db.session.commit() + + db.session.delete(appointment) + db.session.commit() + + version_count = db.session.execute( + text( + "SELECT COUNT(*) FROM appointment_version WHERE appointment_id = :appointment_id" + ), + {"appointment_id": appointment.appointment_id}, + ).scalar_one() + transaction_count = db.session.execute( + text("SELECT COUNT(*) FROM transaction") + ).scalar_one() + + assert version_count >= 2 + assert transaction_count >= 2 diff --git a/api/app/utilities/snowplow.py b/api/app/utilities/snowplow.py index d32703929..67e5656c7 100644 --- a/api/app/utilities/snowplow.py +++ b/api/app/utilities/snowplow.py @@ -23,7 +23,7 @@ from snowplow_tracker import SelfDescribingJson import logging import os -from qsystem import application, my_print +from qsystem import application, db, my_print from datetime import datetime, timezone # Defining String constants to appease SonarQube @@ -46,7 +46,7 @@ def add_citizen(new_citizen, csr): if SnowPlow.call_snowplow_flag: # Set up contexts for the call. - citizen_obj = Citizen.query.get(new_citizen.citizen_id) + citizen_obj = db.session.get(Citizen, new_citizen.citizen_id) citizen = SnowPlow.get_citizen(citizen_obj, csr.counter.counter_name) office = SnowPlow.get_office(new_citizen.office_id) agent = SnowPlow.get_csr(csr, office) @@ -81,7 +81,7 @@ def snowplow_event(citizen_id, csr, schema, period_count = 0, quantity = 0, curr if SnowPlow.call_snowplow_flag: # Set up the contexts for the call. - citizen_obj = Citizen.query.get(citizen_id) + citizen_obj = db.session.get(Citizen, citizen_id) citizen = SnowPlow.get_citizen(citizen_obj, csr.counter.counter_name, svc_number = current_sr_number) office = SnowPlow.get_office(csr.office_id) agent = SnowPlow.get_csr(csr, office) @@ -116,7 +116,7 @@ def snowplow_appointment(citizen_obj, csr, appointment, schema): # If no citizen object, get citizen information. if citizen_obj is None: - citizen_obj = Citizen.query.get(appointment.citizen_id) + citizen_obj = db.session.get(Citizen, appointment.citizen_id) # Online CSR has a default of Counter for Counter Name and csr id of 1000001 if csr is None: @@ -167,7 +167,7 @@ def get_citizen(citizen_obj, counter_name, svc_number = 1): def get_office(id): # Set up office variables. - curr_office = Office.query.get(id) + curr_office = db.session.get(Office, id) office_num = curr_office.office_number office_type = "non-reception" if (curr_office.sb.sb_type == "callbyname") or (curr_office.sb.sb_type == "callbyticket"): @@ -223,10 +223,10 @@ def get_service(service_request): pgm_id = service_request.service.parent_id svc_code = service_request.service.service_code svc_name = service_request.service.service_name - parent = Service.query.get(pgm_id) + parent = db.session.get(Service, pgm_id) pgm_code = parent.service_code pgm_name = parent.service_name - channel = Channel.query.get(service_request.channel_id) + channel = db.session.get(Channel, service_request.channel_id) channel_name = channel.channel_name # Translate channel name to old versions, to avoid major Snowplow changes diff --git a/api/config.py b/api/config.py index 223e10d4e..a1e9f3026 100644 --- a/api/config.py +++ b/api/config.py @@ -16,6 +16,13 @@ "default": "config.LocalConfig" } + +def normalize_database_engine(db_engine): + if db_engine == "postgres": + return "postgresql+psycopg2" + return db_engine + + class BaseConfig(object): # Set up miscellaneous environment variables. @@ -60,7 +67,7 @@ class BaseConfig(object): DB_LONG_RUNNING_QUERY = float(os.getenv("DATABASE_LONG_RUNNING_QUERY", '0.5')) - DB_ENGINE = os.getenv('DATABASE_ENGINE', '') + DB_ENGINE = normalize_database_engine(os.getenv('DATABASE_ENGINE', '')) DB_USER = os.getenv('DATABASE_USERNAME', '') DB_PASSWORD = os.getenv('DATABASE_PASSWORD','') DB_NAME = os.getenv('DATABASE_NAME','') @@ -196,7 +203,7 @@ class LocalConfig(BaseConfig): SQLALCHEMY_ECHO = False SECRET_KEY = "pancakes" - DB_ENGINE = os.getenv('DATABASE_ENGINE', 'postgres') + DB_ENGINE = normalize_database_engine(os.getenv('DATABASE_ENGINE', 'postgresql+psycopg2')) DB_USER = os.getenv('DATABASE_USERNAME', 'postgres') DB_PASSWORD = os.getenv('DATABASE_PASSWORD', 'root') DB_NAME = os.getenv('DATABASE_NAME', 'qsystem') diff --git a/api/manage.py b/api/manage.py index c15f051f5..fac5f955e 100644 --- a/api/manage.py +++ b/api/manage.py @@ -22,8 +22,6 @@ # Alias for Flask CLI auto-discovery (FLASK_APP=manage) app = application -fm.Migrate(application, db) - @application.cli.group('db') def db_cli(): '''Database migration commands.''' diff --git a/api/qsystem.py b/api/qsystem.py index 89c0783d6..9b7c8d865 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -23,7 +23,6 @@ from jose.exceptions import JOSEError from sqlalchemy import event from sqlalchemy.engine import Engine -from sqlalchemy_continuum import make_versioned from flask_migrate import Migrate @@ -47,7 +46,12 @@ def time_string(): ms = now.strftime("%f")[:3] now_string = now.strftime("%Y-%m-%d %H:%M:%S,") return "[" + now_string + ms + "] " + + migrate = Migrate() +db = SQLAlchemy() +cache = Cache() +ma = Marshmallow() application = Flask(__name__, instance_relative_config=True) @@ -64,7 +68,7 @@ def time_string(): appt_limit = application.config['APPOINTMENT_LIMIT_DAYS'] # Set up SQL Alchemy, caching, marshmallow -db = SQLAlchemy(application) +db.init_app(application) query_limit = application.config['DB_LONG_RUNNING_QUERY'] ping_timeout_seconds = application.config['SOCKETIO_PING_TIMEOUT'] ping_interval_seconds = application.config['SOCKETIO_PING_INTERVAL'] @@ -75,11 +79,9 @@ def time_string(): cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': application.config['CACHE_DEFAULT_TIMEOUT']}) cache.init_app(application) -ma = Marshmallow(application) +ma.init_app(application) migrate.init_app(application, db) -make_versioned(user_cls=None, plugins=[]) - # Set up socket io and rabbit mq. socketio = SocketIO(logger=socket_flag, engineio_logger=engine_flag,ping_timeout=ping_timeout_seconds,ping_interval=ping_interval_seconds, cors_allowed_origins=application.config['CORS_ALLOWED_ORIGINS']) @@ -97,29 +99,8 @@ def time_string(): api = Api(application, prefix='/api/v1', doc='/api/v1/') - -# Set up Flask Admin. -from app import admin -flask_admin = Admin(application, name='Admin Console', template_mode='bootstrap3', index_view=admin.HomeView()) -flask_admin.add_view(admin.ChannelModelView) -flask_admin.add_view(admin.CounterModelView) -flask_admin.add_view(admin.CSRModelView) -flask_admin.add_view(admin.CSRGAModelView) -flask_admin.add_view(admin.InvigilatorModelView) -flask_admin.add_view(admin.OfficeModelView) -flask_admin.add_view(admin.OfficeGAModelView) -flask_admin.add_view(admin.RoleModelView) -flask_admin.add_view(admin.ServiceModelView) -flask_admin.add_view(admin.SmartBoardModelView) -flask_admin.add_view(admin.RoomModelView) -flask_admin.add_view(admin.ExamTypeModelView) -flask_admin.add_view(admin.TimeslotModelView) -flask_admin.add_link(admin.LoginMenuLink(name='Login', category='', url="/api/v1/login/")) -flask_admin.add_link(admin.LogoutMenuLink(name='Logout', category='', url="/api/v1/logout/")) - login_manager = LoginManager() login_manager.init_app(application) -import app.auth compress = Compress() compress.init_app(application) @@ -140,32 +121,24 @@ def time_string(): # mail.init_app(application) # application.extensions['mail'].debug = 0 +def log_startup_state(): + if not print_flag: + return + + logging.info("==> DB Engine options") + logging.info(" --> db options: " + str(db.engine)) + logging.info(" --> pool size: " + str(db.engine.pool.size())) + logging.info(" --> max overflow: " + str(db.engine.pool._max_overflow)) + logging.info(" --> echo: " + str(db.engine.echo)) + logging.info(" --> pre ping: " + str(db.engine.pool._pre_ping)) + logging.info(" --> Database URI: " + application.config['SQLALCHEMY_DATABASE_URI_DISPLAY']) + logging.info("") + + logging.info("==> Socket/Engine options") + logging.info(" --> socket: " + os.getenv('LOG_SOCKETIO', '') + '; flag: ' + str(socket_flag)) + logging.info(" --> engine: " + os.getenv('LOG_ENGINEIO', '') + '; flag: ' + str(engine_flag)) + logging.info("") -# Code to determine all db.engine properties and sub-properties, as necessary. -if print_flag: - logging.info("==> All DB Engine options") - for attr in dir(db._engine_options.keys): - logging.info(" --> db._engine_options.keys." + attr + " = " + str(getattr(db._engine_options.keys, attr))) - # print("db.engine.%s = %s") % (attr, getattr(db.engine, attr)) - -# See whether options took. -if print_flag: - logging.info("==> DB Engine options") - logging.info(" --> db options: " + str(db.engine)) - logging.info(" --> pool size: " + str(db.engine.pool.size())) - logging.info(" --> max overflow: " + str(db.engine.pool._max_overflow)) - logging.info(" --> echo: " + str(db.engine.echo)) - logging.info(" --> pre ping: " + str(db.engine.pool._pre_ping)) - logging.info(" --> Database URI: " + application.config['SQLALCHEMY_DATABASE_URI_DISPLAY']) - logging.info("") - - logging.info("==> Socket/Engine options") - logging.info(" --> socket: " + os.getenv('LOG_SOCKETIO', '') + '; flag: ' + str(socket_flag)) - logging.info(" --> engine: " + os.getenv('LOG_ENGINEIO', '') + '; flag: ' + str(engine_flag)) - logging.info("") - -# Get list of available loggers. -if print_flag: logging.info("==> List of available loggers and associated information:") for name in logging.root.manager.loggerDict: temp_logger = logging.getLogger(name) @@ -269,76 +242,100 @@ def get_key(): char_ms = str(time_now.microsecond)[:2] return char_year + char_month + char_day + char_hour + char_minute + char_ms -import app.resources.theq.categories -import app.resources.theq.upload -import app.resources.theq.channels -import app.resources.theq.citizen.citizen_add_to_queue -import app.resources.theq.citizen.citizen_remove_from_queue -import app.resources.theq.citizen.citizen_begin_service -import app.resources.theq.citizen.citizen_detail -import app.resources.theq.citizen.citizen_finish_service -import app.resources.theq.citizen.citizen_generic_invite -import app.resources.theq.citizen.citizen_left -import app.resources.theq.citizen.citizen_list -import app.resources.theq.citizen.citizen_place_on_hold -import app.resources.theq.citizen.citizen_service_requests -import app.resources.theq.citizen.citizen_specific_invite -import app.resources.theq.csrs -import app.resources.theq.csr_states -import app.resources.theq.csr_detail -import app.resources.theq.feedback -import app.resources.theq.health -import app.resources.theq.login -import app.resources.theq.offices -import app.resources.theq.services -import app.resources.theq.service_requests_list -import app.resources.theq.service_requests_detail -import app.resources.theq.smartboard -import app.resources.theq.videofiles -import app.resources.theq.websocket -import app.resources.theq.user.user -import app.resources.theq.user.user_appointments - -import app.resources.bookings.appointment.all_recurring_stat_delete -import app.resources.bookings.appointment.appointment_availability -import app.resources.bookings.appointment.appointment_detail -import app.resources.bookings.appointment.appointment_list -import app.resources.bookings.appointment.appointment_post -import app.resources.bookings.appointment.appointment_draft_post -import app.resources.bookings.appointment.appointment_draft_delete -import app.resources.bookings.appointment.appointment_draft_flush -import app.resources.bookings.appointment.appointment_put -import app.resources.bookings.appointment.appointment_delete -import app.resources.bookings.appointment.appointment_recurring_delete -import app.resources.bookings.appointment.appointment_recurring_put -import app.resources.bookings.booking.booking_delete -import app.resources.bookings.booking.booking_detail -import app.resources.bookings.booking.booking_list -import app.resources.bookings.booking.booking_post -import app.resources.bookings.booking.booking_put -import app.resources.bookings.booking.booking_recurring_delete -import app.resources.bookings.booking.booking_recurring_put -import app.resources.bookings.booking.booking_recurring_stat_delete -import app.resources.bookings.exam.exam_bcmp -import app.resources.bookings.exam.exam_bulk_status -import app.resources.bookings.exam.exam_delete -import app.resources.bookings.exam.exam_detail -import app.resources.bookings.exam.exam_email_invigilator -import app.resources.bookings.exam.exam_list -import app.resources.bookings.exam.exam_post -import app.resources.bookings.exam.exam_put -import app.resources.bookings.exam.exam_export_list -import app.resources.bookings.exam.exam_event_id_detail -import app.resources.bookings.exam.exam_download -import app.resources.bookings.exam.exam_transfer -import app.resources.bookings.exam.exam_upload -import app.resources.bookings.invigilator.invigilator_list -import app.resources.bookings.invigilator.invigilator_list_offsite -import app.resources.bookings.invigilator.invigilator_put -import app.resources.bookings.room.room_list -import app.resources.bookings.exam_type.exam_type_list -import app.resources.bookings.appointment.appointment_reminder_get -import app.resources.bookings.walkin.walkin +with application.app_context(): + log_startup_state() + + from app import admin + + flask_admin = Admin(application, name='Admin Console', template_mode='bootstrap3', index_view=admin.HomeView()) + flask_admin.add_view(admin.ChannelModelView) + flask_admin.add_view(admin.CounterModelView) + flask_admin.add_view(admin.CSRModelView) + flask_admin.add_view(admin.CSRGAModelView) + flask_admin.add_view(admin.InvigilatorModelView) + flask_admin.add_view(admin.OfficeModelView) + flask_admin.add_view(admin.OfficeGAModelView) + flask_admin.add_view(admin.RoleModelView) + flask_admin.add_view(admin.ServiceModelView) + flask_admin.add_view(admin.SmartBoardModelView) + flask_admin.add_view(admin.RoomModelView) + flask_admin.add_view(admin.ExamTypeModelView) + flask_admin.add_view(admin.TimeslotModelView) + flask_admin.add_link(admin.LoginMenuLink(name='Login', category='', url="/api/v1/login/")) + flask_admin.add_link(admin.LogoutMenuLink(name='Logout', category='', url="/api/v1/logout/")) + + import app.auth + + import app.resources.theq.categories + import app.resources.theq.upload + import app.resources.theq.channels + import app.resources.theq.citizen.citizen_add_to_queue + import app.resources.theq.citizen.citizen_remove_from_queue + import app.resources.theq.citizen.citizen_begin_service + import app.resources.theq.citizen.citizen_detail + import app.resources.theq.citizen.citizen_finish_service + import app.resources.theq.citizen.citizen_generic_invite + import app.resources.theq.citizen.citizen_left + import app.resources.theq.citizen.citizen_list + import app.resources.theq.citizen.citizen_place_on_hold + import app.resources.theq.citizen.citizen_service_requests + import app.resources.theq.citizen.citizen_specific_invite + import app.resources.theq.csrs + import app.resources.theq.csr_states + import app.resources.theq.csr_detail + import app.resources.theq.feedback + import app.resources.theq.health + import app.resources.theq.login + import app.resources.theq.offices + import app.resources.theq.services + import app.resources.theq.service_requests_list + import app.resources.theq.service_requests_detail + import app.resources.theq.smartboard + import app.resources.theq.videofiles + import app.resources.theq.websocket + import app.resources.theq.user.user + import app.resources.theq.user.user_appointments + + import app.resources.bookings.appointment.all_recurring_stat_delete + import app.resources.bookings.appointment.appointment_availability + import app.resources.bookings.appointment.appointment_detail + import app.resources.bookings.appointment.appointment_list + import app.resources.bookings.appointment.appointment_post + import app.resources.bookings.appointment.appointment_draft_post + import app.resources.bookings.appointment.appointment_draft_delete + import app.resources.bookings.appointment.appointment_draft_flush + import app.resources.bookings.appointment.appointment_put + import app.resources.bookings.appointment.appointment_delete + import app.resources.bookings.appointment.appointment_recurring_delete + import app.resources.bookings.appointment.appointment_recurring_put + import app.resources.bookings.booking.booking_delete + import app.resources.bookings.booking.booking_detail + import app.resources.bookings.booking.booking_list + import app.resources.bookings.booking.booking_post + import app.resources.bookings.booking.booking_put + import app.resources.bookings.booking.booking_recurring_delete + import app.resources.bookings.booking.booking_recurring_put + import app.resources.bookings.booking.booking_recurring_stat_delete + import app.resources.bookings.exam.exam_bcmp + import app.resources.bookings.exam.exam_bulk_status + import app.resources.bookings.exam.exam_delete + import app.resources.bookings.exam.exam_detail + import app.resources.bookings.exam.exam_email_invigilator + import app.resources.bookings.exam.exam_list + import app.resources.bookings.exam.exam_post + import app.resources.bookings.exam.exam_put + import app.resources.bookings.exam.exam_export_list + import app.resources.bookings.exam.exam_event_id_detail + import app.resources.bookings.exam.exam_download + import app.resources.bookings.exam.exam_transfer + import app.resources.bookings.exam.exam_upload + import app.resources.bookings.invigilator.invigilator_list + import app.resources.bookings.invigilator.invigilator_list_offsite + import app.resources.bookings.invigilator.invigilator_put + import app.resources.bookings.room.room_list + import app.resources.bookings.exam_type.exam_type_list + import app.resources.bookings.appointment.appointment_reminder_get + import app.resources.bookings.walkin.walkin # Hostname for debug purposes diff --git a/api/requirements.txt b/api/requirements.txt index 49cee13a3..46b5136d5 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,4 +1,4 @@ -alembic==1.13.3 +alembic==1.18.4 amqp==5.2.0 aniso8601==9.0.1 astroid==4.0.4 @@ -22,11 +22,11 @@ Flask-Compress==1.17 Flask-Cors==5.0.0 flask-jwt-oidc==0.3.0 Flask-Login==0.6.3 -flask-marshmallow==0.14.0 -Flask-Migrate==3.1.0 +flask-marshmallow==1.4.0 +Flask-Migrate==4.1.0 flask-restx==1.3.0 Flask-SocketIO==5.1.0 -Flask-SQLAlchemy==2.5.1 +Flask-SQLAlchemy==3.1.1 gevent==24.2.1 greenlet==3.0.3 gunicorn==22.0.0 @@ -46,7 +46,7 @@ lazy-object-proxy==1.10.0 Mako==1.3.3 MarkupSafe==2.1.5 marshmallow==3.21.2 -marshmallow-sqlalchemy==0.26.1 +marshmallow-sqlalchemy==1.4.2 mccabe==0.6.1 minio==7.0.4 oauthlib==3.2.2 @@ -77,10 +77,9 @@ rsa==4.9 simple-websocket==1.0.0 six==1.17.0 snowplow-tracker==1.1.0 -SQLAlchemy==1.3.24 -SQLAlchemy-Continuum==1.3.11 -SQLAlchemy-Utc==0.12.0 -SQLAlchemy-Utils==0.41.2 +SQLAlchemy==2.0.48 +SQLAlchemy-Utc==0.14.0 +SQLAlchemy-Utils==0.42.1 toml==0.10.2 tomlkit==0.14.0 typing_extensions==4.15.0 diff --git a/api/scripts/run_sqlalchemy_smoke_tests.sh b/api/scripts/run_sqlalchemy_smoke_tests.sh new file mode 100755 index 000000000..692468c90 --- /dev/null +++ b/api/scripts/run_sqlalchemy_smoke_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +api_dir="$(cd "${script_dir}/.." && pwd)" + +cd "${api_dir}" +python3 -m pytest app/tests -q diff --git a/api/scripts/run_sqlalchemy_warn20_tests.sh b/api/scripts/run_sqlalchemy_warn20_tests.sh new file mode 100755 index 000000000..20c290843 --- /dev/null +++ b/api/scripts/run_sqlalchemy_warn20_tests.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +api_dir="$(cd "${script_dir}/.." && pwd)" + +cd "${api_dir}" +export SQLALCHEMY_WARN_20=1 +python3 -m pytest app/tests -q \ + -W "error::sqlalchemy.exc.RemovedIn20Warning" \ + -W "error::sqlalchemy.exc.SADeprecationWarning" diff --git a/api/setup.cfg b/api/setup.cfg index 3c3d26b0e..580475e0b 100755 --- a/api/setup.cfg +++ b/api/setup.cfg @@ -79,7 +79,8 @@ lines_after_imports = 2 [tool:pytest] -addopts = --cov=src --cov-report html:htmlcov --cov-report xml:coverage.xml -testpaths = tests/unit +addopts = --cov=app --cov-report html:htmlcov --cov-report xml:coverage.xml +testpaths = app/tests +python_files = test*.py filterwarnings = ignore::UserWarning From f7eca866ba85890aafa6276c3d3c1c9ad79ccbdf Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 14:38:42 -0700 Subject: [PATCH 12/81] Update api dependencies --- api/app/admin/timeslot.py | 4 +- .../tests/test_flask_admin_wtforms_compat.py | 66 +++++++++++ api/app/utilities/flask_admin_compat.py | 111 ++++++++++++++++++ api/qsystem.py | 2 + api/requirements.txt | 29 +++-- 5 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 api/app/tests/test_flask_admin_wtforms_compat.py create mode 100644 api/app/utilities/flask_admin_compat.py diff --git a/api/app/admin/timeslot.py b/api/app/admin/timeslot.py index 7a2bc017e..4764600e9 100644 --- a/api/app/admin/timeslot.py +++ b/api/app/admin/timeslot.py @@ -28,10 +28,10 @@ class MultipleSelect2Field(Select2Field): def iter_choices(self): """Iterate over choices especially to check if one of the values is selected.""" if self.allow_blank: - yield (u'__None', self.blank_text, self.data is None) + yield (u'__None', self.blank_text, self.data is None, {}) for value, label in self.choices: - yield (value, label, self.coerce(value) in self.data) + yield (value, label, self.coerce(value) in self.data, {}) def process_data(self, value): """This is called when you create the form with existing data.""" diff --git a/api/app/tests/test_flask_admin_wtforms_compat.py b/api/app/tests/test_flask_admin_wtforms_compat.py new file mode 100644 index 000000000..ad0896301 --- /dev/null +++ b/api/app/tests/test_flask_admin_wtforms_compat.py @@ -0,0 +1,66 @@ +from flask_admin.contrib.sqla.fields import QuerySelectField +from flask_admin.contrib.sqla.validators import Unique +from flask_admin.form.fields import Select2Field +from flask_admin.form.validators import FieldListInputRequired +from wtforms import Form +from wtforms.fields import SelectFieldBase + +from app.utilities.flask_admin_compat import apply_wtforms_compat + + +def test_apply_wtforms_compat_normalizes_legacy_tuple_flags(): + FieldListInputRequired.field_flags = ("required",) + Unique.field_flags = ("unique",) + + apply_wtforms_compat() + + assert FieldListInputRequired.field_flags == {"required": True} + assert Unique.field_flags == {"unique": True} + + +def test_apply_wtforms_compat_normalizes_select2_iter_choices(): + class TestForm(Form): + status = Select2Field(choices=[("1", "Active")]) + + apply_wtforms_compat() + + form = TestForm() + + assert list(form.status.iter_choices()) == [("1", "Active", False, {})] + + +def test_apply_wtforms_compat_normalizes_query_select_iter_choices(): + class Choice: + def __init__(self, identifier, label): + self.identifier = identifier + self.label = label + + class TestForm(Form): + role = QuerySelectField( + query_factory=lambda: [Choice(1, "CSR")], + get_pk=lambda obj: obj.identifier, + get_label=lambda obj: obj.label, + ) + + apply_wtforms_compat() + + form = TestForm() + + assert list(form.role.iter_choices()) == [("1", "CSR", False, {})] + + +def test_apply_wtforms_compat_allows_legacy_select_widgets_to_render(): + class LegacySelectField(SelectFieldBase): + widget = Select2Field.widget + + def iter_choices(self): + yield ("1", "Active", False) + + class TestForm(Form): + status = LegacySelectField() + + apply_wtforms_compat() + + form = TestForm() + + assert 'option value="1"' in form.status() diff --git a/api/app/utilities/flask_admin_compat.py b/api/app/utilities/flask_admin_compat.py new file mode 100644 index 000000000..a2aee9e72 --- /dev/null +++ b/api/app/utilities/flask_admin_compat.py @@ -0,0 +1,111 @@ +"""Compatibility helpers for Flask-Admin on newer WTForms releases.""" + +from collections.abc import Mapping + +from flask_admin._backwards import Markup +from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField +from flask_admin.contrib.sqla.widgets import CheckboxListInput +from flask_admin.contrib.sqla.validators import Unique +from flask_admin.form.fields import Select2Field +from flask_admin.form.validators import FieldListInputRequired +from wtforms.widgets.core import Select, escape, html_params + + +def _normalize_field_flags(validator_class): + """WTForms 3.2 expects validator field_flags to be a mapping.""" + field_flags = getattr(validator_class, "field_flags", {}) + if isinstance(field_flags, Mapping): + return + + validator_class.field_flags = {flag: True for flag in field_flags} + + +def _normalize_iter_choices(field_class): + """WTForms 3.2 expects select choices to include render_kw.""" + if getattr(field_class, "_wtforms_compat_choices_patched", False): + return + + original_iter_choices = field_class.iter_choices + + def wrapped_iter_choices(self): + for choice in original_iter_choices(self): + yield _normalize_choice(choice) + + field_class.iter_choices = wrapped_iter_choices + field_class._wtforms_compat_choices_patched = True + + +def _normalize_choice(choice): + if len(choice) == 3: + return (*choice, {}) + return choice + + +def _patch_select_widget(): + if getattr(Select, "_wtforms_compat_choices_patched", False): + return + + def wrapped_call(self, field, **kwargs): + kwargs.setdefault("id", field.id) + if self.multiple: + kwargs["multiple"] = True + + flags = getattr(field, "flags", {}) + for key in dir(flags): + if key in self.validation_attrs and key not in kwargs: + kwargs[key] = getattr(flags, key) + + select_params = html_params(name=field.name, **kwargs) + html = [f"") + return Markup("".join(html)) + + Select.__call__ = wrapped_call + Select._wtforms_compat_choices_patched = True + + +def _patch_checkbox_list_widget(): + if getattr(CheckboxListInput, "_wtforms_compat_choices_patched", False): + return + + def wrapped_call(self, field, **kwargs): + items = [] + for choice in field.iter_choices(): + value, label, selected, _render_kw = _normalize_choice(choice) + args = { + "id": value, + "name": field.name, + "label": escape(label), + "selected": " checked" if selected else "", + } + items.append(self.template % args) + return Markup("".join(items)) + + CheckboxListInput.__call__ = wrapped_call + CheckboxListInput._wtforms_compat_choices_patched = True + + +def apply_wtforms_compat(): + """Patch known Flask-Admin validators that still use legacy tuple flags.""" + for validator_class in (FieldListInputRequired, Unique): + _normalize_field_flags(validator_class) + + for field_class in (Select2Field, QuerySelectField, QuerySelectMultipleField): + _normalize_iter_choices(field_class) + + _patch_select_widget() + _patch_checkbox_list_widget() diff --git a/api/qsystem.py b/api/qsystem.py index 9b7c8d865..5905861e8 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -19,6 +19,7 @@ from functools import wraps from sqlalchemy.exc import SQLAlchemyError from app.exceptions import AuthError +from app.utilities.flask_admin_compat import apply_wtforms_compat from flask_jwt_oidc.exceptions import AuthError as JwtAuthError from jose.exceptions import JOSEError from sqlalchemy import event @@ -55,6 +56,7 @@ def time_string(): application = Flask(__name__, instance_relative_config=True) +apply_wtforms_compat() # Make sure we 404 when the trailing slash is not present on ALL routes application.url_map.strict_slashes = True diff --git a/api/requirements.txt b/api/requirements.txt index 46b5136d5..81c8e3336 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,6 +1,8 @@ alembic==1.18.4 amqp==5.2.0 aniso8601==9.0.1 +argon2-cffi==25.1.0 +argon2-cffi-bindings==25.1.0 astroid==4.0.4 async-timeout==4.0.3 attrs==23.2.0 @@ -9,8 +11,10 @@ blinker==1.8.2 Brotli==1.1.0 cachelib==0.13.0 certifi==2024.7.4 +cffi==2.0.0 charset-normalizer==3.3.2 click==8.1.7 +cryptography==46.0.5 decorator==5.0.9 dill==0.4.1 ecdsa==0.19.0 @@ -27,9 +31,9 @@ Flask-Migrate==4.1.0 flask-restx==1.3.0 Flask-SocketIO==5.1.0 Flask-SQLAlchemy==3.1.1 -gevent==24.2.1 -greenlet==3.0.3 -gunicorn==22.0.0 +gevent==25.9.1 +greenlet==3.3.2 +gunicorn==25.2.0 h11==0.14.0 idna==3.7 ijson==2.6.1 @@ -41,14 +45,14 @@ itsdangerous==2.2.0 Jinja2==3.1.6 jsonschema==4.22.0 jsonschema-specifications==2023.12.1 -kombu==5.1.0 +kombu==5.6.2 lazy-object-proxy==1.10.0 Mako==1.3.3 MarkupSafe==2.1.5 marshmallow==3.21.2 marshmallow-sqlalchemy==1.4.2 mccabe==0.6.1 -minio==7.0.4 +minio==7.2.20 oauthlib==3.2.2 packaging==24.0 pkgutil_resolve_name==1.3.10 @@ -56,24 +60,27 @@ platformdirs==4.9.4 pluggy==1.6.0 psycopg2-binary==2.9.11 pyasn1==0.6.0 +pycparser==3.0 +pycryptodome==3.23.0 Pygments==2.19.2 +PyJWT==2.12.1 pylint==4.0.5 pyparsing==3.0.7 pytest==9.0.2 python-dateutil==2.9.0.post0 python-dotenv==1.2.2 python-editor==1.0.4 -python-engineio==4.9.0 +python-engineio==4.13.1 python-jose==3.3.0 python-magic==0.4.27 -python-socketio==5.11.2 +python-socketio==5.16.1 pytz==2019.3 -redis==4.5.3 +redis==7.4.0 referencing==0.35.1 requests==2.32.2 requests-oauthlib==1.3.1 rpds-py==0.18.1 -rsa==4.9 +rsa==4.9.1 simple-websocket==1.0.0 six==1.17.0 snowplow-tracker==1.1.0 @@ -83,12 +90,14 @@ SQLAlchemy-Utils==0.42.1 toml==0.10.2 tomlkit==0.14.0 typing_extensions==4.15.0 +tzdata==2025.3 urllib3==1.26.19 vine==5.1.0 Werkzeug==2.3.8 wrapt==1.12.1 wsproto==1.2.0 -WTForms==3.0.0 +WTForms==3.2.1 zipp==3.19.1 zope.event==5.0 zope.interface==6.3 +zstandard==0.25.0 From 5cd29e149e2a0815452a780917d04cbb86cf7158 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 15:32:47 -0700 Subject: [PATCH 13/81] Upgrade to Flask 3 --- api/app/admin/index.py | 2 +- api/app/resources/theq/websocket.py | 2 +- api/app/tests/conftest.py | 8 ++ api/app/tests/test_flask3_smoke.py | 78 ++++++++++++++++++++ api/app/utilities/flask_admin_compat.py | 2 +- api/config.py | 2 + api/qsystem.py | 28 ++++++- api/requirements.txt | 29 ++------ api/templates/admin/base.html | 90 ++++++++++------------- api/templates/admin/home.html | 7 ++ api/templates/office/office_create.html | 7 +- api/templates/office/office_edit.html | 7 +- api/templates/office/officega_create.html | 7 +- api/templates/office/officega_edit.html | 7 +- 14 files changed, 176 insertions(+), 100 deletions(-) create mode 100644 api/app/tests/test_flask3_smoke.py create mode 100644 api/templates/admin/home.html diff --git a/api/app/admin/index.py b/api/app/admin/index.py index 3b59e9bf9..c1d110270 100644 --- a/api/app/admin/index.py +++ b/api/app/admin/index.py @@ -20,7 +20,7 @@ class HomeView(AdminIndexView): @expose('/') def index(self): - return self.render('admin/base.html') + return self.render('admin/home.html') def get_url(self, endpoint, **kwargs): new_kwargs = dict(kwargs, _external=True, _scheme=application.config['PREFERRED_URL_SCHEME']) diff --git a/api/app/resources/theq/websocket.py b/api/app/resources/theq/websocket.py index 8e0c5d407..b55c7a6ea 100644 --- a/api/app/resources/theq/websocket.py +++ b/api/app/resources/theq/websocket.py @@ -19,8 +19,8 @@ from app.auth.auth import jwt from app.models.theq import CSR, Office from app.utilities.auth_util import get_username +from flask_jwt_oidc import AuthError from qsystem import socketio, my_print -from flask_jwt_oidc.exceptions import AuthError @socketio.on('joinRoom') diff --git a/api/app/tests/conftest.py b/api/app/tests/conftest.py index 86013a83f..b0f49bf8b 100644 --- a/api/app/tests/conftest.py +++ b/api/app/tests/conftest.py @@ -64,6 +64,10 @@ def postgres_database(): "DATABASE_USERNAME", "DATABASE_PASSWORD", "DATABASE_NAME", + "JWT_OIDC_WELL_KNOWN_CONFIG", + "JWT_OIDC_JWKS_URI", + "JWT_OIDC_ISSUER", + "JWT_OIDC_AUDIENCE", ) } @@ -76,6 +80,10 @@ def postgres_database(): "DATABASE_USERNAME": settings["user"], "DATABASE_PASSWORD": settings["password"], "DATABASE_NAME": database_name, + "JWT_OIDC_WELL_KNOWN_CONFIG": "", + "JWT_OIDC_JWKS_URI": "https://example.com/jwks.json", + "JWT_OIDC_ISSUER": "https://example.com/", + "JWT_OIDC_AUDIENCE": "queue-api-tests", } ) diff --git a/api/app/tests/test_flask3_smoke.py b/api/app/tests/test_flask3_smoke.py new file mode 100644 index 000000000..ef255a213 --- /dev/null +++ b/api/app/tests/test_flask3_smoke.py @@ -0,0 +1,78 @@ +import inspect +from types import SimpleNamespace + + +def _unwrap(func): + return inspect.unwrap(func) + + +def test_routes_command_lists_admin_and_healthz(cli_runner): + result = cli_runner.invoke(args=["routes"]) + + assert result.exit_code == 0, result.output + assert "/admin/" in result.output + assert "/api/v1/healthz/" in result.output + + +def test_admin_index_renders_without_bootstrap3_assets(client): + response = client.get("/admin/") + + assert response.status_code == 200 + + body = response.get_data(as_text=True) + + assert "Admin Console" in body + assert "bootstrap3" not in body + + +def test_login_resource_redirects_authenticated_users_to_admin(app, monkeypatch): + from app.resources.theq import login as login_module + + fake_csr = SimpleNamespace(username="tester") + logged_in = [] + + monkeypatch.setattr(login_module, "get_username", lambda: "tester@idir") + monkeypatch.setattr(login_module.CSR, "find_by_username", lambda username: fake_csr) + monkeypatch.setattr(login_module, "login_user", lambda user: logged_in.append(user)) + + handler = _unwrap(login_module.Login.get) + + with app.test_request_context("/api/v1/login/"): + response = handler(login_module.Login()) + + assert logged_in == [fake_csr] + assert response.status_code == 302 + assert response.location.endswith("/admin/") + + +def test_join_room_handler_emits_success_for_authenticated_csr(app, monkeypatch): + del app + + from app.resources.theq import websocket + + joined_rooms = [] + emitted_events = [] + fake_csr = SimpleNamespace( + username="tester", + office=SimpleNamespace(office_name="Victoria"), + ) + + monkeypatch.setattr(websocket, "get_username", lambda: "tester@idir") + monkeypatch.setattr(websocket.CSR, "find_by_username", lambda username: fake_csr) + monkeypatch.setattr(websocket, "join_room", lambda room: joined_rooms.append(room)) + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + monkeypatch.setattr(websocket, "request", SimpleNamespace(sid="socket-1")) + + handler = _unwrap(websocket.on_join) + handler({}) + + assert joined_rooms == ["Victoria"] + assert emitted_events == [ + ("joinRoomSuccess", {"sucess": True}), + ("get_Csr_State_IDs", {"success": True}), + ("update_customer_list", {"success": True}), + ] diff --git a/api/app/utilities/flask_admin_compat.py b/api/app/utilities/flask_admin_compat.py index a2aee9e72..52406055d 100644 --- a/api/app/utilities/flask_admin_compat.py +++ b/api/app/utilities/flask_admin_compat.py @@ -2,12 +2,12 @@ from collections.abc import Mapping -from flask_admin._backwards import Markup from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField from flask_admin.contrib.sqla.widgets import CheckboxListInput from flask_admin.contrib.sqla.validators import Unique from flask_admin.form.fields import Select2Field from flask_admin.form.validators import FieldListInputRequired +from markupsafe import Markup from wtforms.widgets.core import Select, escape, html_params diff --git a/api/config.py b/api/config.py index a1e9f3026..71018e95a 100644 --- a/api/config.py +++ b/api/config.py @@ -172,6 +172,8 @@ class BaseConfig(object): # JWT_OIDC Settings JWT_OIDC_WELL_KNOWN_CONFIG = os.getenv('JWT_OIDC_WELL_KNOWN_CONFIG') + JWT_OIDC_JWKS_URI = os.getenv('JWT_OIDC_JWKS_URI') + JWT_OIDC_ISSUER = os.getenv('JWT_OIDC_ISSUER') JWT_OIDC_ALGORITHMS = os.getenv('JWT_OIDC_ALGORITHMS', 'RS256') JWT_OIDC_AUDIENCE = os.getenv('JWT_OIDC_AUDIENCE') JWT_OIDC_CLIENT_SECRET = os.getenv('JWT_OIDC_CLIENT_SECRET', '') diff --git a/api/qsystem.py b/api/qsystem.py index 5905861e8..d44b697c5 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -4,10 +4,12 @@ import traceback import os import datetime +from urllib.error import URLError from config import configure_app, configure_logging, debug_level_to_debug_string from flask import Flask from flask_admin import Admin +from flask_admin.theme import Bootstrap4Theme from flask_caching import Cache from flask_compress import Compress from flask_cors import CORS @@ -20,7 +22,7 @@ from sqlalchemy.exc import SQLAlchemyError from app.exceptions import AuthError from app.utilities.flask_admin_compat import apply_wtforms_compat -from flask_jwt_oidc.exceptions import AuthError as JwtAuthError +from flask_jwt_oidc import AuthError as JwtAuthError from jose.exceptions import JOSEError from sqlalchemy import event from sqlalchemy.engine import Engine @@ -249,7 +251,12 @@ def get_key(): from app import admin - flask_admin = Admin(application, name='Admin Console', template_mode='bootstrap3', index_view=admin.HomeView()) + flask_admin = Admin( + application, + name='Admin Console', + theme=Bootstrap4Theme(), + index_view=admin.HomeView(), + ) flask_admin.add_view(admin.ChannelModelView) flask_admin.add_view(admin.CounterModelView) flask_admin.add_view(admin.CSRModelView) @@ -426,7 +433,22 @@ def get_roles(a_dict): app.config['JWT_ROLE_CALLBACK'] = get_roles - jwt_manager.init_app(app) + try: + jwt_manager.init_app(app) + except URLError: + well_known_config = app.config.get('JWT_OIDC_WELL_KNOWN_CONFIG') + jwks_uri = app.config.get('JWT_OIDC_JWKS_URI') + issuer = app.config.get('JWT_OIDC_ISSUER') + + if not (well_known_config and jwks_uri and issuer): + raise + + app.logger.warning( + 'JWT well-known config could not be reached; retrying with direct JWKS and issuer settings.' + ) + app.config['JWT_OIDC_WELL_KNOWN_CONFIG'] = None + jwt_manager.init_app(app) + app.config['JWT_OIDC_WELL_KNOWN_CONFIG'] = well_known_config setup_jwt_manager(application) diff --git a/api/requirements.txt b/api/requirements.txt index 81c8e3336..c4148b58e 100755 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -3,11 +3,9 @@ amqp==5.2.0 aniso8601==9.0.1 argon2-cffi==25.1.0 argon2-cffi-bindings==25.1.0 -astroid==4.0.4 -async-timeout==4.0.3 attrs==23.2.0 bidict==0.23.1 -blinker==1.8.2 +Blinker==1.9.0 Brotli==1.1.0 cachelib==0.13.0 certifi==2024.7.4 @@ -19,17 +17,17 @@ decorator==5.0.9 dill==0.4.1 ecdsa==0.19.0 filelock==3.0.12 -Flask==2.3.3 -Flask-Admin==1.6.1 +Flask==3.1.3 +Flask-Admin==2.0.2 Flask-Caching==2.3.1 Flask-Compress==1.17 Flask-Cors==5.0.0 -flask-jwt-oidc==0.3.0 +flask-jwt-oidc==0.8.0 Flask-Login==0.6.3 flask-marshmallow==1.4.0 Flask-Migrate==4.1.0 -flask-restx==1.3.0 -Flask-SocketIO==5.1.0 +flask-restx==1.3.2 +Flask-SocketIO==5.6.1 Flask-SQLAlchemy==3.1.1 gevent==25.9.1 greenlet==3.3.2 @@ -37,10 +35,6 @@ gunicorn==25.2.0 h11==0.14.0 idna==3.7 ijson==2.6.1 -importlib_metadata==7.1.0 -importlib_resources==6.4.0 -iniconfig==2.0.0 -isort==8.0.1 itsdangerous==2.2.0 Jinja2==3.1.6 jsonschema==4.22.0 @@ -51,25 +45,19 @@ Mako==1.3.3 MarkupSafe==2.1.5 marshmallow==3.21.2 marshmallow-sqlalchemy==1.4.2 -mccabe==0.6.1 minio==7.2.20 oauthlib==3.2.2 packaging==24.0 -pkgutil_resolve_name==1.3.10 platformdirs==4.9.4 -pluggy==1.6.0 psycopg2-binary==2.9.11 pyasn1==0.6.0 pycparser==3.0 pycryptodome==3.23.0 Pygments==2.19.2 PyJWT==2.12.1 -pylint==4.0.5 pyparsing==3.0.7 -pytest==9.0.2 python-dateutil==2.9.0.post0 python-dotenv==1.2.2 -python-editor==1.0.4 python-engineio==4.13.1 python-jose==3.3.0 python-magic==0.4.27 @@ -82,22 +70,19 @@ requests-oauthlib==1.3.1 rpds-py==0.18.1 rsa==4.9.1 simple-websocket==1.0.0 -six==1.17.0 snowplow-tracker==1.1.0 SQLAlchemy==2.0.48 SQLAlchemy-Utc==0.14.0 SQLAlchemy-Utils==0.42.1 toml==0.10.2 -tomlkit==0.14.0 typing_extensions==4.15.0 tzdata==2025.3 urllib3==1.26.19 vine==5.1.0 -Werkzeug==2.3.8 +Werkzeug==3.1.6 wrapt==1.12.1 wsproto==1.2.0 WTForms==3.2.1 -zipp==3.19.1 zope.event==5.0 zope.interface==6.3 zstandard==0.25.0 diff --git a/api/templates/admin/base.html b/api/templates/admin/base.html index f334c51ee..29eb2c376 100644 --- a/api/templates/admin/base.html +++ b/api/templates/admin/base.html @@ -1,7 +1,7 @@ {% import 'admin/layout.html' as layout with context -%} {% import 'admin/static.html' as admin_static with context %} - + {% block title %}{% if admin_view.category %}{{ admin_view.category }} - {% endif %}{{ admin_view.name }} - {{ admin_view.admin.name }}{% endblock %} {% block head_meta %} @@ -12,71 +12,57 @@ {% endblock %} {% block head_css %} - - {%if config.get('FLASK_ADMIN_SWATCH', 'default') == 'default' %} - - {%endif%} - + + {% if theme.swatch == 'default' %} + + {% endif %} + + {% if admin_view.extra_css %} {% for css_url in admin_view.extra_css %} - + {% endfor %} {% endif %} - {% endblock %} {% block head %} {% endblock %} {% block head_tail %} {% endblock %} - - {% block page_body %} -
- {% block messages %} - {{ layout.messages() }} - {% endblock %} - {# store the jinja2 context for form_rules rendering logic #} - {% set render_ctx = h.resolve_ctx() %} - {% block body %}{% endblock %} - {% endblock %} + +{% block page_body %} +
+ {% block messages %} + {{ layout.messages() }} + {% endblock %} + + {% set render_ctx = h.resolve_ctx() %} + + {% block body %}{% endblock %} +
+{% endblock %} - {% block tail_js %} - - - - - +{% block tail_js %} + + + + + + + + + {% if admin_view.extra_js %} - {% for js_url in admin_view.extra_js %} - - {% endfor %} + {% for js_url in admin_view.extra_js %} + + {% endfor %} {% endif %} - {% endblock %} +{% endblock %} + {% block tail %} {% endblock %} - - {% if not current_user.is_authenticated %} -
- {{ layout.menu_links() }} - {{ current_user.username }} -
- {% endif %} diff --git a/api/templates/admin/home.html b/api/templates/admin/home.html new file mode 100644 index 000000000..16d60d8be --- /dev/null +++ b/api/templates/admin/home.html @@ -0,0 +1,7 @@ +{% extends 'admin/master.html' %} + +{% block body %} +
+

{{ admin_view.admin.name }}

+
+{% endblock %} diff --git a/api/templates/office/office_create.html b/api/templates/office/office_create.html index 578d64869..9569e8d7b 100644 --- a/api/templates/office/office_create.html +++ b/api/templates/office/office_create.html @@ -3,10 +3,7 @@ {{ super() }} {% endblock %} {% block tail_js %} - - - - + {{ super() }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/api/templates/office/office_edit.html b/api/templates/office/office_edit.html index bedfaf03a..d44c4eacd 100644 --- a/api/templates/office/office_edit.html +++ b/api/templates/office/office_edit.html @@ -3,10 +3,7 @@ {{ super() }} {% endblock %} {% block tail_js %} - - - - + {{ super() }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/api/templates/office/officega_create.html b/api/templates/office/officega_create.html index c44267df5..4cde4015b 100644 --- a/api/templates/office/officega_create.html +++ b/api/templates/office/officega_create.html @@ -3,10 +3,7 @@ {{ super() }} {% endblock %} {% block tail_js %} - - - - + {{ super() }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/api/templates/office/officega_edit.html b/api/templates/office/officega_edit.html index 8d4a4e71e..5d40a3861 100644 --- a/api/templates/office/officega_edit.html +++ b/api/templates/office/officega_edit.html @@ -3,10 +3,7 @@ {{ super() }} {% endblock %} {% block tail_js %} - - - - + {{ super() }} -{% endblock %} \ No newline at end of file +{% endblock %} From d945fbd09389dace76142fad4e4d05fada9a757a Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 17:45:15 -0700 Subject: [PATCH 14/81] Migrate to uv + update dependencies --- .devcontainer/docker-compose.yml | 5 +- .devcontainer/postCreateCommand.sh | 27 +- api/app/README.md | 12 +- api/app/admin/room.py | 5 +- api/app/models/theq/public_user.py | 6 +- .../appointment/appointment_availability.py | 6 +- .../bookings/appointment/appointment_list.py | 5 +- .../booking/booking_recurring_delete.py | 5 +- .../booking/booking_recurring_stat_delete.py | 6 +- .../bookings/exam/exam_export_list.py | 8 +- api/app/resources/bookings/walkin/walkin.py | 27 +- api/app/resources/theq/csrs.py | 6 +- api/app/resources/theq/feedback.py | 4 +- api/app/services/availability_service.py | 16 +- .../tests/test_dependency_modernization.py | 62 + api/app/utilities/bcmp_service.py | 6 +- api/app/utilities/date_util.py | 7 +- api/app/utilities/email.py | 4 +- api/app/utilities/notification_email.py | 5 +- api/app/utilities/sms.py | 20 +- api/app/utilities/timezone_utils.py | 27 + api/manage.py | 6 +- api/pyproject.toml | 116 ++ api/requirements.txt | 896 ++++++++- api/requirements_dev.txt | 826 ++++++++- api/scripts/export_requirements.sh | 9 + api/scripts/run_sqlalchemy_smoke_tests.sh | 2 +- api/scripts/run_sqlalchemy_warn20_tests.sh | 2 +- api/setup.cfg | 35 - api/setup.py | 52 - api/uv.lock | 1632 +++++++++++++++++ 31 files changed, 3558 insertions(+), 287 deletions(-) create mode 100644 api/app/tests/test_dependency_modernization.py create mode 100644 api/app/utilities/timezone_utils.py create mode 100644 api/pyproject.toml mode change 100755 => 100644 api/requirements.txt mode change 100755 => 100644 api/requirements_dev.txt create mode 100644 api/scripts/export_requirements.sh delete mode 100755 api/setup.py create mode 100644 api/uv.lock diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 91624e0ad..7fe5c2c10 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -48,6 +48,7 @@ services: # For container "x11" used by Cypress. DISPLAY: ':14' LIBGL_ALWAYS_INDIRECT: '0' + UV_PROJECT_ENVIRONMENT: '/workspace/api/.venv' init: true @@ -58,7 +59,7 @@ services: volumes: - x11:/tmp/.X11-unix:rw - ..:/workspace:cached - - api-env:/workspace/api/env + - api-venv:/workspace/api/.venv - appointment-frontend-node-modules:/workspace/appointment-frontend/node_modules - frontend-node-modules:/workspace/frontend/node_modules @@ -102,6 +103,6 @@ services: volumes: postgres-data: x11: - api-env: + api-venv: appointment-frontend-node-modules: frontend-node-modules: diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 0edc59e6c..c9077ee62 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -96,16 +96,14 @@ check_setting () { install_api_deps () { ( cd api - # Ensure the env directory is owned by the current user - if [ -d env ]; then - sudo chown -R $(id -u):$(id -g) env + export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv" + # Ensure the environment directory is owned by the current user + if [ -d .venv ]; then + sudo chown -R $(id -u):$(id -g) .venv fi - python3 -m venv env || echo_failure "Failed to create virtual environment." - - # Activate the virtual environment and install dependencies - source env/bin/activate || echo_failure "Failed to activate virtual environment." - python -m pip install --upgrade pip -q || echo_failure "Failed to upgrade pip." - pip install -r requirements_dev.txt --progress-bar off || echo_failure "Failed to install dependencies." + python3 -m pip install --upgrade pip -q || echo_failure "Failed to upgrade pip." + python3 -m pip install uv -q || echo_failure "Failed to install uv." + uv sync --group dev || echo_failure "Failed to sync API dependencies." ) } @@ -145,19 +143,18 @@ install_frontend_deps bootstrap_database () { ( cd api - source env/bin/activate - python manage.py db upgrade - pip install -r requirements.txt + export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv" + uv run python manage.py db upgrade # If there is nothing in the CSR table, we're probably starting with a # clean database and need to bootstrap it with default data. - python manage.py migrate_db + uv run python manage.py migrate_db read -p "Enter your IDIR to check if db is bootstrapped: " SEARCH_USER COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ -U postgres -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';" -t) if [ "$COUNT" -eq 0 ]; then - python manage.py bootstrap - echo "$SEARCH_USER" | python manage.py adduser + uv run python manage.py bootstrap + echo "$SEARCH_USER" | uv run python manage.py adduser fi ) } diff --git a/api/app/README.md b/api/app/README.md index da35b8fa2..bd2ef99ea 100644 --- a/api/app/README.md +++ b/api/app/README.md @@ -1,8 +1,14 @@ # Back-end Development Guide -Generate new migrations +Sync the project environment ``` -python3 manage.py db migrate -python3 manage.py db upgrade +uv sync --group dev +``` + +Generate new migrations + +```bash +uv run python manage.py db migrate +uv run python manage.py db upgrade ``` diff --git a/api/app/admin/room.py b/api/app/admin/room.py index dd471e71f..48a836cdd 100644 --- a/api/app/admin/room.py +++ b/api/app/admin/room.py @@ -20,7 +20,8 @@ from flask_login import current_user from qsystem import db from datetime import datetime -import pytz + +from app.utilities.timezone_utils import as_utc class RoomConfig(Base): @@ -42,7 +43,7 @@ def on_model_change(self, form, model, is_created): if not is_created: room_id = get_mdict_item_or_list(request.args, 'id') today = datetime.now() - today_aware = pytz.utc.localize(today) + today_aware = as_utc(today) booking_room = Booking.query.filter_by(room_id=room_id)\ .filter(Booking.start_time > today_aware).count() diff --git a/api/app/models/theq/public_user.py b/api/app/models/theq/public_user.py index be9647020..7af252a4b 100644 --- a/api/app/models/theq/public_user.py +++ b/api/app/models/theq/public_user.py @@ -15,9 +15,7 @@ from app.models.theq import Base, Citizen from qsystem import cache, db from app.models.bookings.appointments import Appointment -from datetime import datetime -from pytz import timezone -import pytz +from datetime import datetime, timezone class PublicUser(Base): @@ -52,7 +50,7 @@ def find_by_user_id(cls, user_id): @classmethod def find_appointments_by_username(cls, username: str): """Find all appointments for the user.""" - today = datetime.now(timezone('UTC')) + today = datetime.now(timezone.utc) query = db.session.query(Appointment) \ .join(Citizen) \ diff --git a/api/app/resources/bookings/appointment/appointment_availability.py b/api/app/resources/bookings/appointment/appointment_availability.py index 2b66683e5..17dbd1c5a 100644 --- a/api/app/resources/bookings/appointment/appointment_availability.py +++ b/api/app/resources/bookings/appointment/appointment_availability.py @@ -14,7 +14,6 @@ import datetime, logging -import pytz from flask_restx import Resource from flask import request from sqlalchemy import exc @@ -22,8 +21,9 @@ from app.models.theq import Office from app.models.theq import Service from app.services import AvailabilityService -from qsystem import api +from qsystem import api, db from app.auth.auth import jwt +from app.utilities.timezone_utils import get_timezone @api.route("/offices//slots/", methods=["GET"]) @@ -36,7 +36,7 @@ def get(self, office_id: int): appointments_days_limit = office.appointments_days_limit # Dictionary to store the available slots per day - tz = pytz.timezone(office.timezone.timezone_name) + tz = get_timezone(office.timezone.timezone_name) # today's date and time today = datetime.datetime.now().astimezone(tz) diff --git a/api/app/resources/bookings/appointment/appointment_list.py b/api/app/resources/bookings/appointment/appointment_list.py index cf5a10285..2f7b289e3 100644 --- a/api/app/resources/bookings/appointment/appointment_list.py +++ b/api/app/resources/bookings/appointment/appointment_list.py @@ -13,8 +13,7 @@ limitations under the License.''' import logging -import pytz -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from flask_restx import Resource from sqlalchemy import exc from app.models.bookings import Appointment @@ -38,7 +37,7 @@ def get(self): # today's date and time dt = datetime.now() upper_dt = dt - timedelta(days=appt_limit_int) - filter_date = pytz.utc.localize(upper_dt) + filter_date = upper_dt.replace(tzinfo=timezone.utc) # print("filter_date",filter_date) try: appointments = Appointment.query.filter_by(office_id=csr.office_id)\ diff --git a/api/app/resources/bookings/booking/booking_recurring_delete.py b/api/app/resources/bookings/booking/booking_recurring_delete.py index f84f9a4d5..985e9797c 100644 --- a/api/app/resources/bookings/booking/booking_recurring_delete.py +++ b/api/app/resources/bookings/booking/booking_recurring_delete.py @@ -18,7 +18,7 @@ from app.models.theq import CSR from qsystem import api, db from datetime import datetime, timedelta, date -import logging, pytz +import logging from app.utilities.auth_util import Role, get_username from app.auth.auth import jwt @@ -27,7 +27,6 @@ class BookingRecurringDelete(Resource): booking_schema = BookingSchema - timezone = pytz.timezone("US/Pacific") @jwt.has_one_of_roles([Role.internal_user.value]) def delete(self, id): @@ -54,4 +53,4 @@ def delete(self, id): db.session.delete(booking) db.session.commit() - return {},204 \ No newline at end of file + return {},204 diff --git a/api/app/resources/bookings/booking/booking_recurring_stat_delete.py b/api/app/resources/bookings/booking/booking_recurring_stat_delete.py index 30c68fc74..ec93975e8 100644 --- a/api/app/resources/bookings/booking/booking_recurring_stat_delete.py +++ b/api/app/resources/bookings/booking/booking_recurring_stat_delete.py @@ -18,7 +18,7 @@ from app.models.theq import CSR from qsystem import api, db from datetime import datetime, timedelta, date -import logging, pytz +import logging from app.utilities.auth_util import Role, get_username from app.auth.auth import jwt @@ -27,7 +27,6 @@ class BookingRecurringDelete(Resource): booking_schema = BookingSchema - timezone = pytz.timezone("US/Pacific") @jwt.has_one_of_roles([Role.internal_user.value]) def delete(self, id): @@ -58,7 +57,6 @@ def delete(self, id): class BookingRecurringDelete(Resource): booking_schema = BookingSchema - timezone = pytz.timezone("US/Pacific") @jwt.has_one_of_roles([Role.internal_user.value]) def delete(self, id): @@ -78,4 +76,4 @@ def delete(self, id): db.session.delete(booking) db.session.commit() - return {},204 \ No newline at end of file + return {},204 diff --git a/api/app/resources/bookings/exam/exam_export_list.py b/api/app/resources/bookings/exam/exam_export_list.py index 189e4bea0..169e1ce1e 100644 --- a/api/app/resources/bookings/exam/exam_export_list.py +++ b/api/app/resources/bookings/exam/exam_export_list.py @@ -22,11 +22,11 @@ from app.schemas.theq import OfficeSchema, TimezoneSchema from qsystem import api, my_print from datetime import datetime, timedelta, timezone -import pytz import csv import io from app.utilities.auth_util import Role, get_username from app.auth.auth import jwt +from app.utilities.timezone_utils import get_timezone, localize @api.route("/exams/export/", methods=["GET"]) @@ -62,10 +62,10 @@ def get(self): csr_office = Office.query.filter(Office.office_id == csr.office_id).first() csr_timezone = Timezone.query.filter(Timezone.timezone_id == csr_office.timezone_id).first() csr_timename = csr_timezone.timezone_name - timezone = pytz.timezone(csr_timename) - start_local = timezone.localize(start_date) + timezone = get_timezone(csr_timename) + start_local = localize(start_date, csr_timename) end_date += timedelta(days=1) - end_local = timezone.localize(end_date) + end_local = localize(end_date, csr_timename) exams = Exam.query.join(Booking, Exam.booking_id == Booking.booking_id) \ .filter(Booking.start_time >= start_local) \ diff --git a/api/app/resources/bookings/walkin/walkin.py b/api/app/resources/bookings/walkin/walkin.py index 79055af70..ba8ef0b1f 100644 --- a/api/app/resources/bookings/walkin/walkin.py +++ b/api/app/resources/bookings/walkin/walkin.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.''' -import logging, pytz +import logging from datetime import datetime, timedelta, timezone from flask import request, g from flask_restx import Resource @@ -29,6 +29,7 @@ from app.utilities.sms import send_walkin_reminder_sms from sqlalchemy.dialects import postgresql from sqlalchemy.orm import raiseload, joinedload, contains_eager +from app.utilities.timezone_utils import UTC, as_utc, get_timezone # Defining String constants to appease SonarQube api_down_const = 'API is down' @@ -88,7 +89,7 @@ def get_my_office_timezone(self, citizen=False, office=False): my_office_data = self.office_schema.dump(my_office) if my_office_data: my_time_zone = my_office_data['timezone']['timezone_name'] - local_timezone = pytz.timezone(my_time_zone) + local_timezone = get_timezone(my_time_zone) return local_timezone def am_i_on_hold(self, citizen): @@ -143,9 +144,9 @@ def process_all_citizen_in_q(self, result, citizen, am_on_hold, local_timezone): if (not_booked_flag and each.get('cs', False)) and each['cs'].get('cs_state_name', '') == 'Active': each_time_obj = datetime.strptime(each['start_time'], '%Y-%m-%dT%H:%M:%SZ') # start - local_datetime_start = each_time_obj.replace(tzinfo=pytz.utc).astimezone(local_timezone) + local_datetime_start = each_time_obj.replace(tzinfo=UTC).astimezone(local_timezone) #end - local_datetime_end = citizen.start_time.replace(tzinfo=pytz.utc).astimezone(local_timezone) + local_datetime_end = citizen.start_time.replace(tzinfo=UTC).astimezone(local_timezone) if am_on_hold or local_datetime_start <= local_datetime_end: data_dict['flag'] = 'walkin_app' walkin_app.append(data_dict) @@ -163,18 +164,8 @@ def get_all_app_from_agenda_panel(self, citizen=False, office=False): if office_id: past_hour = datetime.now(timezone.utc) - timedelta(minutes=15) future_hour = datetime.now(timezone.utc) + timedelta(minutes=15) - if past_hour.tzinfo is None: - # If it's naive, localize to UTC - local_past = pytz.utc.localize(past_hour) - else: - # If it's already timezone-aware, no need to localize - local_past = past_hour - if future_hour.tzinfo is None: - # Only localize if the datetime is naive - local_future = pytz.utc.localize(future_hour) - else: - # If it's already timezone-aware, no need to localize - local_future = future_hour + local_past = as_utc(past_hour) + local_future = as_utc(future_hour) # getting agenda panel app appointments = Appointment.query.filter_by(office_id=office_id)\ .filter(Appointment.start_time <= local_future)\ @@ -196,7 +187,7 @@ def process_agenda_panel(self, result_in_book, local_timezone): if (len(data_dict['start_time']) >= 3) and ':' in data_dict['start_time'][-3]: data_dict['start_time'] = '{}{}'.format(data_dict['start_time'][:-3], data_dict['start_time'][-2:]) utc_datetime = datetime.strptime(data_dict['start_time'], '%Y-%m-%dT%H:%M:%S%z') - local_datetime = utc_datetime.replace(tzinfo=pytz.utc) + local_datetime = utc_datetime.replace(tzinfo=UTC) local_datetime = local_datetime.astimezone(local_timezone) data_dict['start_time'] = local_datetime.strftime("%m/%d/%Y, %H:%M:%S") booked_not_checkin.append(data_dict) @@ -398,4 +389,4 @@ def get(self, id): return {} except exc.SQLAlchemyError as exception: logging.exception(exception) - return {'message': api_down_const}, 500 \ No newline at end of file + return {'message': api_down_const}, 500 diff --git a/api/app/resources/theq/csrs.py b/api/app/resources/theq/csrs.py index 5abd13746..60af29a4b 100644 --- a/api/app/resources/theq/csrs.py +++ b/api/app/resources/theq/csrs.py @@ -21,9 +21,9 @@ from app.models.theq import Citizen, CSR, Period, ServiceReq, SRState from app.schemas.bookings import ExamSchema, ExamTypeSchema from app.schemas.theq import CitizenSchema, CSRSchema -import pytz from app.utilities.auth_util import Role, get_username from app.auth.auth import jwt +from app.utilities.timezone_utils import get_timezone, localize @api.route("/csrs/", methods=["GET"]) @@ -58,7 +58,7 @@ class CsrSelf(Resource): citizen_schema = CitizenSchema(many=True) exam_schema = ExamSchema(many=True) exam_type_schema = ExamTypeSchema() - timezone = pytz.timezone("US/Pacific") + timezone = get_timezone("America/Vancouver") back_office_display = application.config['BACK_OFFICE_DISPLAY'] recurring_feature_flag = application.config['RECURRING_FEATURE_FLAG'] @@ -75,7 +75,7 @@ def get(self): db.session.add(csr) active_sr_state = SRState.get_state_by_name("Active") today = datetime.now() - start_date = self.timezone.localize(today).date() + start_date = localize(today, "America/Vancouver").date() active_citizens = Citizen.query \ .join(Citizen.service_reqs) \ diff --git a/api/app/resources/theq/feedback.py b/api/app/resources/theq/feedback.py index 6d6096d6e..0bcd16c37 100644 --- a/api/app/resources/theq/feedback.py +++ b/api/app/resources/theq/feedback.py @@ -24,6 +24,8 @@ from app.utilities.auth_util import Role, has_any_role from app.auth.auth import jwt +request_timeout_seconds = 30 + @api.route("/feedback/", methods=['POST']) class Feedback(Resource): @@ -94,7 +96,7 @@ def send_to_rocket_chat(feedback_message): } params = json.dumps(feedback_json_data).encode('utf8') try: - result = requests.post(url, params) + result = requests.post(url, json=feedback_json_data, timeout=request_timeout_seconds) except Exception as err: return {"message": "Error posting to Rocket Chat. " + str(err)}, 400 diff --git a/api/app/services/availability_service.py b/api/app/services/availability_service.py index 380bc761e..c2987df38 100644 --- a/api/app/services/availability_service.py +++ b/api/app/services/availability_service.py @@ -15,13 +15,13 @@ import logging, datetime from typing import Dict -import pytz from sqlalchemy import exc from app.models.bookings import Appointment from app.models.theq import Office from app.models.theq import Service from app.utilities.date_util import add_delta_to_time, day_indexes +from app.utilities.timezone_utils import get_timezone from app.utilities.yesno import YesNo @@ -45,7 +45,7 @@ def get_available_slots(office: Office, days: [datetime], format_time: bool = Tr service_is_dltk = service and service.is_dlkt == YesNo.YES # Dictionary to store the available slots per day - tz = pytz.timezone(office.timezone.timezone_name) + tz = get_timezone(office.timezone.timezone_name) # today's date and time today = datetime.datetime.now().astimezone(tz) @@ -148,8 +148,9 @@ def get_available_slots(office: Office, days: [datetime], format_time: bool = Tr @staticmethod def has_available_slots(office: Office, start_time:datetime, end_time: datetime, service: Service): """Return if there is any available slot for the time period for the office.""" - start_time = start_time.astimezone(pytz.timezone(office.timezone.timezone_name)) - end_time = end_time.astimezone(pytz.timezone(office.timezone.timezone_name)) + office_timezone = get_timezone(office.timezone.timezone_name) + start_time = start_time.astimezone(office_timezone) + end_time = end_time.astimezone(office_timezone) available_day_slots = AvailabilityService.get_available_slots(office=office, days=[start_time], format_time=False, service=service) @@ -167,12 +168,13 @@ def has_available_slots(office: Office, start_time:datetime, end_time: datetime, def group_appointments(appointments, timezone: str): filtered_appointments = {} for app in appointments: - formatted_date = app.start_time.astimezone(pytz.timezone(timezone)).strftime('%m/%d/%Y') + app_timezone = get_timezone(timezone) + formatted_date = app.start_time.astimezone(app_timezone).strftime('%m/%d/%Y') if not filtered_appointments.get(formatted_date, None): filtered_appointments[formatted_date] = [] filtered_appointments[formatted_date].append({ - 'start_time': app.start_time.astimezone(pytz.timezone(timezone)).time(), - 'end_time': app.end_time.astimezone(pytz.timezone(timezone)).time(), + 'start_time': app.start_time.astimezone(app_timezone).time(), + 'end_time': app.end_time.astimezone(app_timezone).time(), 'blackout_flag': app.blackout_flag == 'Y' or app.stat_flag, 'is_dlkt': (app.service.is_dlkt == YesNo.YES) if app.service else False }) diff --git a/api/app/tests/test_dependency_modernization.py b/api/app/tests/test_dependency_modernization.py new file mode 100644 index 000000000..28e94d43c --- /dev/null +++ b/api/app/tests/test_dependency_modernization.py @@ -0,0 +1,62 @@ +from datetime import datetime + +from flask import Flask + +from app.utilities.date_util import add_delta_to_time, current_pacific_time +from app.utilities.notification_email import send_email +from app.utilities.timezone_utils import as_utc, get_timezone, localize + + +def test_localize_preserves_dst_offset_for_vancouver(): + localized = localize(datetime(2026, 3, 8, 1, 30), "America/Vancouver") + + assert localized.tzinfo == get_timezone("America/Vancouver") + assert localized.utcoffset().total_seconds() == -8 * 3600 + + +def test_as_utc_marks_naive_datetimes_as_utc(): + converted = as_utc(datetime(2026, 3, 8, 9, 30)) + + assert converted.utcoffset().total_seconds() == 0 + assert converted.isoformat() == "2026-03-08T09:30:00+00:00" + + +def test_add_delta_to_time_returns_zoneinfo_backed_time(): + shifted = add_delta_to_time(datetime.strptime("08:30", "%H:%M").time(), "America/Vancouver", minutes=30) + + assert shifted.strftime("%H:%M") == "09:00" + assert shifted.tzinfo == get_timezone("America/Vancouver") + + +def test_current_pacific_time_uses_vancouver_zone(): + pacific_now = current_pacific_time() + + assert pacific_now.tzinfo == get_timezone("America/Vancouver") + + +def test_send_email_posts_json_payload_with_timeout(monkeypatch): + app = Flask(__name__) + app.config["NOTIFICATIONS_EMAIL_ENDPOINT"] = "https://example.com/email" + + recorded = {} + + class Response: + def raise_for_status(self): + return None + + def fake_post(url, headers=None, json=None, timeout=None): + recorded["url"] = url + recorded["headers"] = headers + recorded["json"] = json + recorded["timeout"] = timeout + return Response() + + monkeypatch.setattr("app.utilities.notification_email.requests.post", fake_post) + + with app.app_context(): + send_email("token-123", "Subject", "citizen@example.com", "noreply@example.com", "

Hello

") + + assert recorded["url"] == "https://example.com/email" + assert recorded["headers"]["Authorization"] == "Bearer token-123" + assert recorded["json"]["subject"] == "Subject" + assert recorded["timeout"] == 30 diff --git a/api/app/utilities/bcmp_service.py b/api/app/utilities/bcmp_service.py index c0429f450..084620f5a 100644 --- a/api/app/utilities/bcmp_service.py +++ b/api/app/utilities/bcmp_service.py @@ -4,8 +4,8 @@ from qsystem import application, my_print from app.utilities.document_service import DocumentService from datetime import datetime -import pytz from dateutil import parser +from app.utilities.timezone_utils import get_timezone class BCMPService: base_url = application.config['BCMP_BASE_URL'] @@ -128,10 +128,10 @@ def create_group_exam_bcmp(self, exam, booking, candiate_list, pesticide_office, my_print(" ==> create_group_exam_bcmp url: %s" % url) office_name = None - time_zone = pytz.timezone('America/Vancouver') + time_zone = get_timezone('America/Vancouver') if pesticide_office: office_name = pesticide_office.office_name - time_zone = pytz.timezone(pesticide_office.timezone.timezone_name) + time_zone = get_timezone(pesticide_office.timezone.timezone_name) my_print(exam.expiry_date.strftime("%a %b %d, %Y at %-I:%M %p")) exam_text = None diff --git a/api/app/utilities/date_util.py b/api/app/utilities/date_util.py index e6680b8be..9873beb89 100644 --- a/api/app/utilities/date_util.py +++ b/api/app/utilities/date_util.py @@ -13,7 +13,8 @@ limitations under the License.''' import datetime as dt from datetime import timedelta, time -import pytz + +from app.utilities.timezone_utils import get_timezone, localize days_mapping = { 'Monday': 1, @@ -38,7 +39,7 @@ def add_delta_to_time(time: time, timezone, minutes: int = 0, seconds: int = 0): else: delta_time -= timedelta(seconds=seconds) - return delta_time.replace(tzinfo=pytz.timezone(timezone)).time() + return localize(delta_time, timezone).timetz() def day_indexes(days): @@ -51,4 +52,4 @@ def day_indexes(days): def current_pacific_time(): """Return current time as in pacific zone.""" - return dt.datetime.now().astimezone(pytz.timezone('US/Pacific')) + return dt.datetime.now(get_timezone('America/Vancouver')) diff --git a/api/app/utilities/email.py b/api/app/utilities/email.py index ab32f222d..fceca6606 100644 --- a/api/app/utilities/email.py +++ b/api/app/utilities/email.py @@ -15,13 +15,13 @@ import re from datetime import datetime -import pytz from flask import current_app from jinja2 import Environment, FileSystemLoader from .notification_email import send_email from app.models.bookings import Appointment from app.models.theq import Citizen, Office +from app.utilities.timezone_utils import get_timezone ENV = Environment(loader=FileSystemLoader('.'), autoescape=True) @@ -120,7 +120,7 @@ def is_valid_email(email: str): def formatted_date(dt: datetime, timezone): - dt_local = dt.astimezone(pytz.timezone(timezone.timezone_name)) + dt_local = dt.astimezone(get_timezone(timezone.timezone_name)) return dt_local.strftime('%B %d, %Y at %I:%M %p'), dt_local.strftime('%B %d, %Y') diff --git a/api/app/utilities/notification_email.py b/api/app/utilities/notification_email.py index 858e4d877..8b7b7265b 100644 --- a/api/app/utilities/notification_email.py +++ b/api/app/utilities/notification_email.py @@ -18,6 +18,8 @@ import requests from flask import current_app +request_timeout_seconds = 30 + def send_email(token, subject, email, sender, html_body): """Send the email asynchronously, using the given details.""" @@ -34,7 +36,8 @@ def send_email(token, subject, email, sender, html_body): } response = requests.post(send_email_endpoint, headers={'Content-Type': 'application/json', 'Authorization': f'Bearer {token}'}, - data=json.dumps(payload)) + json=payload, + timeout=request_timeout_seconds) response.raise_for_status() diff --git a/api/app/utilities/sms.py b/api/app/utilities/sms.py index 7f64786da..62c2ed2d7 100644 --- a/api/app/utilities/sms.py +++ b/api/app/utilities/sms.py @@ -16,15 +16,16 @@ from datetime import datetime import json -import pytz import requests from flask import current_app from app.models.bookings import Appointment from app.models.theq import Office, PublicUser, Citizen +from app.utilities.timezone_utils import get_timezone # Defining String constants to appease SonarQube app_json_const = 'application/json' +request_timeout_seconds = 30 def send_sms(appointment: Appointment, office: Office, timezone, user: PublicUser, token: str): """Send confirmation email""" @@ -35,13 +36,14 @@ def send_sms(appointment: Appointment, office: Office, timezone, user: PublicUse display_name: str = user.display_name if user else '' # For CSR appointment user is None requests.post(notifications_endpoint, headers={'Content-Type': app_json_const, 'Authorization': f'Bearer {token}'}, - data=json.dumps([{ + json=[{ 'user_telephone': telephone, 'display_name': display_name, 'location': office.office_name, 'formatted_date': format_sms_date(appointment.start_time, timezone), 'office_telephone': office.telephone - }])) + }], + timeout=request_timeout_seconds) except Exception as exc: logging.exception("Error on sms sending - %s", exc) @@ -57,7 +59,7 @@ def is_valid_phone(phone_number: str): def format_sms_date(dt: datetime, timezone): - dt_local = dt.astimezone(pytz.timezone(timezone.timezone_name)) + dt_local = dt.astimezone(get_timezone(timezone.timezone_name)) return dt_local.strftime('%A, %B %d at %I:%M %p') @@ -77,12 +79,13 @@ def send_walkin_spot_confirmation_sms(citizen: Citizen, url, token: str): try: requests.post(notifications_endpoint, headers={'Content-Type': app_json_const, 'Authorization': f'Bearer {token}'}, - data=json.dumps([{ + json=[{ 'user_telephone': telephone, 'url': url, 'ticket_number': citizen.ticket_number, "type": "CHECKIN_CONFIRMATION" - }])) + }], + timeout=request_timeout_seconds) return True except Exception as exc: logging.exception('Error on sms sending - %s', exc) @@ -99,11 +102,12 @@ def send_walkin_reminder_sms(citizen: Citizen, office: Office, token: str): msg = "We’re ready! Please come inside and speak to a Service BC Representative" requests.post(notifications_endpoint, headers={'Content-Type': app_json_const, 'Authorization': f'Bearer {token}'}, - data=json.dumps([{ + json=[{ 'user_telephone': telephone, 'message': office.check_in_reminder_msg if office.check_in_reminder_msg else msg, "type": "CUSTOM" - }])) + }], + timeout=request_timeout_seconds) return True except Exception as exc: logging.exception('Error on sms sending - %s', exc) diff --git a/api/app/utilities/timezone_utils.py b/api/app/utilities/timezone_utils.py new file mode 100644 index 000000000..eb53886db --- /dev/null +++ b/api/app/utilities/timezone_utils.py @@ -0,0 +1,27 @@ +"""Timezone helpers built on Python's stdlib zoneinfo support.""" + +from datetime import datetime, timezone +from zoneinfo import ZoneInfo + + +UTC = timezone.utc + + +def get_timezone(timezone_name: str) -> ZoneInfo: + """Return a ZoneInfo instance for the given timezone name.""" + return ZoneInfo(timezone_name) + + +def localize(value: datetime, timezone_name: str) -> datetime: + """Attach a timezone to a naive datetime or convert an aware datetime.""" + zone = get_timezone(timezone_name) + if value.tzinfo is None: + return value.replace(tzinfo=zone) + return value.astimezone(zone) + + +def as_utc(value: datetime) -> datetime: + """Attach UTC to a naive datetime or convert an aware datetime to UTC.""" + if value.tzinfo is None: + return value.replace(tzinfo=UTC) + return value.astimezone(UTC) diff --git a/api/manage.py b/api/manage.py index fac5f955e..30a22329c 100644 --- a/api/manage.py +++ b/api/manage.py @@ -6,9 +6,9 @@ data suitable for development or demos. Usage: - python manage.py db - python manage.py migrate_db - python manage.py bootstrap # ***will wipe your database*** + uv run python manage.py db + uv run python manage.py migrate_db + uv run python manage.py bootstrap # ***will wipe your database*** ''' import logging diff --git a/api/pyproject.toml b/api/pyproject.toml new file mode 100644 index 000000000..7343af330 --- /dev/null +++ b/api/pyproject.toml @@ -0,0 +1,116 @@ +[build-system] +requires = ["setuptools>=69", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "queue_api" +version = "0.0.0" +description = "Queue management API" +requires-python = ">=3.11,<3.12" +dependencies = [ + "alembic==1.18.4", + "amqp==5.2.0", + "aniso8601==9.0.1", + "argon2-cffi==25.1.0", + "argon2-cffi-bindings==25.1.0", + "attrs==23.2.0", + "bidict==0.23.1", + "Blinker==1.9.0", + "Brotli==1.1.0", + "cachelib==0.13.0", + "certifi==2024.7.4", + "cffi==2.0.0", + "charset-normalizer==3.3.2", + "click==8.1.7", + "cryptography==46.0.5", + "decorator==5.0.9", + "dill==0.4.1", + "ecdsa==0.19.0", + "filelock==3.0.12", + "Flask==3.1.3", + "Flask-Admin==2.0.2", + "Flask-Caching==2.3.1", + "Flask-Compress==1.17", + "Flask-Cors==5.0.0", + "flask-jwt-oidc==0.8.0", + "Flask-Login==0.6.3", + "flask-marshmallow==1.4.0", + "Flask-Migrate==4.1.0", + "flask-restx==1.3.2", + "Flask-SocketIO==5.6.1", + "Flask-SQLAlchemy==3.1.1", + "gevent==25.9.1", + "greenlet==3.3.2", + "gunicorn==25.2.0", + "h11==0.14.0", + "idna==3.7", + "ijson==2.6.1", + "itsdangerous==2.2.0", + "Jinja2==3.1.6", + "jsonschema==4.22.0", + "jsonschema-specifications==2023.12.1", + "kombu==5.6.2", + "lazy-object-proxy==1.10.0", + "Mako==1.3.3", + "MarkupSafe==2.1.5", + "marshmallow==3.21.2", + "marshmallow-sqlalchemy==1.4.2", + "minio==7.2.20", + "oauthlib==3.2.2", + "packaging==24.0", + "platformdirs==4.9.4", + "psycopg2-binary==2.9.11", + "pyasn1==0.6.0", + "pycparser==3.0", + "pycryptodome==3.23.0", + "Pygments==2.19.2", + "PyJWT==2.12.1", + "pyparsing==3.0.7", + "python-dateutil==2.9.0.post0", + "python-dotenv==1.2.2", + "python-engineio==4.13.1", + "python-jose==3.3.0", + "python-magic==0.4.27", + "python-socketio==5.16.1", + "redis==7.4.0", + "referencing==0.35.1", + "requests==2.32.5", + "requests-oauthlib==1.3.1", + "rpds-py==0.18.1", + "rsa==4.9.1", + "simple-websocket==1.0.0", + "snowplow-tracker==1.1.0", + "SQLAlchemy==2.0.48", + "SQLAlchemy-Utc==0.14.0", + "SQLAlchemy-Utils==0.42.1", + "toml==0.10.2", + "typing_extensions==4.15.0", + "tzdata==2025.3", + "urllib3==2.6.3", + "vine==5.1.0", + "Werkzeug==3.1.6", + "wrapt==1.12.1", + "wsproto==1.2.0", + "WTForms==3.2.1", + "zope.event==5.0", + "zope.interface==6.3", + "zstandard==0.25.0", +] + +[dependency-groups] +dev = [ + "astroid==4.0.4", + "isort==8.0.1", + "mccabe==0.6.1", + "pluggy==1.6.0", + "pylint==4.0.5", + "pytest==9.0.2", + "pytest-cov==7.0.0", +] + +[tool.setuptools] +include-package-data = true +py-modules = ["config", "manage", "qsystem", "version", "wsgi"] + +[tool.setuptools.packages.find] +include = ["app*"] diff --git a/api/requirements.txt b/api/requirements.txt old mode 100755 new mode 100644 index c4148b58e..ae6fb16d8 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,88 +1,808 @@ -alembic==1.18.4 -amqp==5.2.0 -aniso8601==9.0.1 -argon2-cffi==25.1.0 -argon2-cffi-bindings==25.1.0 -attrs==23.2.0 -bidict==0.23.1 -Blinker==1.9.0 -Brotli==1.1.0 -cachelib==0.13.0 -certifi==2024.7.4 -cffi==2.0.0 -charset-normalizer==3.3.2 -click==8.1.7 -cryptography==46.0.5 -decorator==5.0.9 -dill==0.4.1 -ecdsa==0.19.0 -filelock==3.0.12 -Flask==3.1.3 -Flask-Admin==2.0.2 -Flask-Caching==2.3.1 -Flask-Compress==1.17 -Flask-Cors==5.0.0 -flask-jwt-oidc==0.8.0 -Flask-Login==0.6.3 -flask-marshmallow==1.4.0 -Flask-Migrate==4.1.0 -flask-restx==1.3.2 -Flask-SocketIO==5.6.1 -Flask-SQLAlchemy==3.1.1 -gevent==25.9.1 -greenlet==3.3.2 -gunicorn==25.2.0 -h11==0.14.0 -idna==3.7 -ijson==2.6.1 -itsdangerous==2.2.0 -Jinja2==3.1.6 -jsonschema==4.22.0 -jsonschema-specifications==2023.12.1 -kombu==5.6.2 -lazy-object-proxy==1.10.0 -Mako==1.3.3 -MarkupSafe==2.1.5 -marshmallow==3.21.2 -marshmallow-sqlalchemy==1.4.2 -minio==7.2.20 -oauthlib==3.2.2 -packaging==24.0 -platformdirs==4.9.4 -psycopg2-binary==2.9.11 -pyasn1==0.6.0 -pycparser==3.0 -pycryptodome==3.23.0 -Pygments==2.19.2 -PyJWT==2.12.1 -pyparsing==3.0.7 -python-dateutil==2.9.0.post0 -python-dotenv==1.2.2 -python-engineio==4.13.1 -python-jose==3.3.0 -python-magic==0.4.27 -python-socketio==5.16.1 -pytz==2019.3 -redis==7.4.0 -referencing==0.35.1 -requests==2.32.2 -requests-oauthlib==1.3.1 -rpds-py==0.18.1 -rsa==4.9.1 -simple-websocket==1.0.0 -snowplow-tracker==1.1.0 -SQLAlchemy==2.0.48 -SQLAlchemy-Utc==0.14.0 -SQLAlchemy-Utils==0.42.1 -toml==0.10.2 -typing_extensions==4.15.0 -tzdata==2025.3 -urllib3==1.26.19 -vine==5.1.0 -Werkzeug==3.1.6 -wrapt==1.12.1 -wsproto==1.2.0 -WTForms==3.2.1 -zope.event==5.0 -zope.interface==6.3 -zstandard==0.25.0 +# This file was autogenerated by uv via the following command: +# uv export --frozen --cache-dir /tmp/uv-cache --format requirements-txt --no-emit-project --output-file requirements.txt +alembic==1.18.4 \ + --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ + --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc + # via + # flask-migrate + # queue-api +amqp==5.2.0 \ + --hash=sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637 \ + --hash=sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd + # via + # kombu + # queue-api +aniso8601==9.0.1 \ + --hash=sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f \ + --hash=sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973 + # via + # flask-restx + # queue-api +argon2-cffi==25.1.0 \ + --hash=sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1 \ + --hash=sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 + # via + # minio + # queue-api +argon2-cffi-bindings==25.1.0 \ + --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ + --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ + --hash=sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44 \ + --hash=sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2 \ + --hash=sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0 \ + --hash=sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98 \ + --hash=sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500 \ + --hash=sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94 \ + --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ + --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ + --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a + # via + # argon2-cffi + # queue-api +astroid==4.0.4 \ + --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ + --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 + # via pylint +async-timeout==5.0.1 ; python_full_version < '3.11.3' \ + --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ + --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 + # via redis +attrs==23.2.0 \ + --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ + --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 + # via + # jsonschema + # queue-api + # referencing +bidict==0.23.1 \ + --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ + --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 + # via + # python-socketio + # queue-api +blinker==1.9.0 \ + --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ + --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc + # via + # flask + # flask-socketio + # queue-api +brotli==1.1.0 \ + --hash=sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9 \ + --hash=sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757 \ + --hash=sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0 \ + --hash=sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd \ + --hash=sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50 \ + --hash=sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265 \ + --hash=sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327 \ + --hash=sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd \ + --hash=sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724 \ + --hash=sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8 \ + --hash=sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc \ + --hash=sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61 \ + --hash=sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1 \ + --hash=sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f \ + --hash=sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6 \ + --hash=sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf \ + --hash=sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b + # via + # flask-compress + # queue-api +brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ + --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ + --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ + --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \ + --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \ + --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \ + --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \ + --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \ + --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \ + --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \ + --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1 + # via flask-compress +cachelib==0.13.0 \ + --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ + --hash=sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516 + # via + # flask-caching + # flask-jwt-oidc + # queue-api +certifi==2024.7.4 \ + --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ + --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 + # via + # minio + # queue-api + # requests +cffi==2.0.0 \ + --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ + --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ + --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ + --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ + --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ + --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ + --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ + --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ + --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ + --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ + --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ + --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d + # via + # argon2-cffi-bindings + # brotlicffi + # cryptography + # gevent + # queue-api +charset-normalizer==3.3.2 \ + --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ + --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ + --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ + --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ + --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ + --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ + --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ + --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ + --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ + --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ + --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ + --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ + --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ + --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ + --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ + --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ + --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 + # via + # queue-api + # requests +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de + # via + # flask + # flask-socketio + # queue-api +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # click + # pylint + # pytest +coverage==7.13.5 \ + --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \ + --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \ + --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \ + --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \ + --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \ + --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ + --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \ + --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \ + --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \ + --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \ + --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \ + --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \ + --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \ + --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \ + --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ + --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ + --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b + # via pytest-cov +cryptography==46.0.5 \ + --hash=sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72 \ + --hash=sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235 \ + --hash=sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356 \ + --hash=sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257 \ + --hash=sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad \ + --hash=sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4 \ + --hash=sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c \ + --hash=sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614 \ + --hash=sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed \ + --hash=sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31 \ + --hash=sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229 \ + --hash=sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0 \ + --hash=sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731 \ + --hash=sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b \ + --hash=sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263 \ + --hash=sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595 \ + --hash=sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1 \ + --hash=sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48 \ + --hash=sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76 \ + --hash=sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18 \ + --hash=sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d \ + --hash=sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1 \ + --hash=sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7 \ + --hash=sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82 \ + --hash=sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4 \ + --hash=sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c \ + --hash=sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d \ + --hash=sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a \ + --hash=sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a \ + --hash=sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d \ + --hash=sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b \ + --hash=sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9 \ + --hash=sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da \ + --hash=sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2 \ + --hash=sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2 + # via + # flask-jwt-oidc + # queue-api +decorator==5.0.9 \ + --hash=sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323 \ + --hash=sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5 + # via queue-api +dill==0.4.1 \ + --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ + --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa + # via + # pylint + # queue-api +ecdsa==0.19.0 \ + --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ + --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 + # via + # python-jose + # queue-api +filelock==3.0.12 \ + --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ + --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 + # via queue-api +flake8-import-order==0.19.2 \ + --hash=sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2 \ + --hash=sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c + # via zimports +flask==3.1.3 \ + --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ + --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c + # via + # flask-admin + # flask-caching + # flask-compress + # flask-cors + # flask-jwt-oidc + # flask-login + # flask-marshmallow + # flask-migrate + # flask-restx + # flask-socketio + # flask-sqlalchemy + # queue-api +flask-admin==2.0.2 \ + --hash=sha256:1d06aec7efee957972b43f6b08a0bd08d5f4cf9a337d4ece2f17c98abc2a214e \ + --hash=sha256:4b3c44068de0fe4630dfcd190cc11231cbbdd7bac315c74c55d1764087b8b273 + # via queue-api +flask-caching==2.3.1 \ + --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ + --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 + # via queue-api +flask-compress==1.17 \ + --hash=sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8 \ + --hash=sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20 + # via queue-api +flask-cors==5.0.0 \ + --hash=sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef \ + --hash=sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc + # via queue-api +flask-jwt-oidc==0.8.0 \ + --hash=sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0 \ + --hash=sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4 + # via queue-api +flask-login==0.6.3 \ + --hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \ + --hash=sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d + # via queue-api +flask-marshmallow==1.4.0 \ + --hash=sha256:98c90a253052c72d2ddddc925539ac33bbd780c6fba86478ffe18e3b89d8b471 \ + --hash=sha256:b758fc2c428d0cbee6fd0ccf0d55524fe9e426a86a177dcc0fc8cd71ad4b7c59 + # via queue-api +flask-migrate==4.1.0 \ + --hash=sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d \ + --hash=sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d + # via queue-api +flask-restx==1.3.2 \ + --hash=sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5 \ + --hash=sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b + # via queue-api +flask-socketio==5.6.1 \ + --hash=sha256:51a3f71b28b4476c650829607e3a993e076034db6c3cc31f718f0a4b45939d42 \ + --hash=sha256:fe5bd995c3ed4da9a98f335d0d830fa1a19d84a64789f6265642a671fdacaeac + # via queue-api +flask-sqlalchemy==3.1.1 \ + --hash=sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0 \ + --hash=sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312 + # via + # flask-migrate + # queue-api +gevent==25.9.1 \ + --hash=sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7 \ + --hash=sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457 \ + --hash=sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff \ + --hash=sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586 \ + --hash=sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235 \ + --hash=sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd \ + --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ + --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 + # via queue-api +greenlet==3.3.2 \ + --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ + --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ + --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ + --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ + --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ + --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ + --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ + --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ + --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ + --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 + # via + # gevent + # queue-api + # sqlalchemy +gunicorn==25.2.0 \ + --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ + --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc + # via queue-api +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via + # queue-api + # wsproto +idna==3.7 \ + --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ + --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 + # via + # queue-api + # requests +ijson==2.6.1 \ + --hash=sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0 + # via queue-api +importlib-resources==6.5.2 \ + --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ + --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec + # via flask-restx +iniconfig==2.3.0 \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + # via pytest +isort==8.0.1 \ + --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ + --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 + # via pylint +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 + # via + # flask + # queue-api +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # flask + # flask-admin + # flask-socketio + # queue-api +jsonschema==4.22.0 \ + --hash=sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7 \ + --hash=sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802 + # via + # flask-restx + # queue-api +jsonschema-specifications==2023.12.1 \ + --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ + --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c + # via + # jsonschema + # queue-api +kombu==5.6.2 \ + --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \ + --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93 + # via queue-api +lazy-object-proxy==1.10.0 \ + --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \ + --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \ + --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \ + --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \ + --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \ + --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \ + --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \ + --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \ + --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c + # via queue-api +mako==1.3.3 \ + --hash=sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40 \ + --hash=sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73 + # via + # alembic + # queue-api +markupsafe==2.1.5 \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b + # via + # flask + # flask-admin + # jinja2 + # mako + # queue-api + # werkzeug + # wtforms +marshmallow==3.21.2 \ + --hash=sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1 \ + --hash=sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56 + # via + # flask-marshmallow + # marshmallow-sqlalchemy + # queue-api +marshmallow-sqlalchemy==1.4.2 \ + --hash=sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c \ + --hash=sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40 + # via queue-api +mccabe==0.6.1 \ + --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ + --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f + # via pylint +minio==7.2.20 \ + --hash=sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598 \ + --hash=sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e + # via queue-api +oauthlib==3.2.2 \ + --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ + --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 + # via + # queue-api + # requests-oauthlib +packaging==24.0 \ + --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ + --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 + # via + # gunicorn + # kombu + # marshmallow + # pytest + # queue-api +platformdirs==4.9.4 \ + --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 + # via + # pylint + # queue-api +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + # via + # pytest + # pytest-cov +psycopg2-binary==2.9.11 \ + --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \ + --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \ + --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \ + --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \ + --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \ + --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \ + --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \ + --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \ + --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \ + --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \ + --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ + --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 + # via queue-api +pyasn1==0.6.0 \ + --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ + --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 + # via + # python-jose + # queue-api + # rsa +pycodestyle==2.14.0 \ + --hash=sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783 \ + --hash=sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d + # via flake8-import-order +pycparser==3.0 \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via + # cffi + # queue-api +pycryptodome==3.23.0 \ + --hash=sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c \ + --hash=sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f \ + --hash=sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27 \ + --hash=sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef \ + --hash=sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886 \ + --hash=sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a \ + --hash=sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490 \ + --hash=sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b \ + --hash=sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2 \ + --hash=sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575 \ + --hash=sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843 \ + --hash=sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa + # via + # minio + # queue-api +pyflakes==3.4.0 \ + --hash=sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58 \ + --hash=sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f + # via zimports +pygments==2.19.2 \ + --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ + --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b + # via + # pytest + # queue-api +pyjwt==2.12.1 \ + --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ + --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b + # via + # flask-jwt-oidc + # queue-api +pylint==4.0.5 \ + --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ + --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c +pyparsing==3.0.7 \ + --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \ + --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484 + # via queue-api +pytest==9.0.2 \ + --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ + --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 + # via pytest-cov +pytest-cov==7.0.0 \ + --hash=sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1 \ + --hash=sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via queue-api +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via queue-api +python-engineio==4.13.1 \ + --hash=sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066 \ + --hash=sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399 + # via + # python-socketio + # queue-api +python-jose==3.3.0 \ + --hash=sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a \ + --hash=sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a + # via queue-api +python-magic==0.4.27 \ + --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ + --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 + # via queue-api +python-socketio==5.16.1 \ + --hash=sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35 \ + --hash=sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89 + # via + # flask-socketio + # queue-api +redis==7.4.0 \ + --hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \ + --hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec + # via queue-api +referencing==0.35.1 \ + --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ + --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de + # via + # flask-restx + # jsonschema + # jsonschema-specifications + # queue-api +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via + # queue-api + # requests-oauthlib + # snowplow-tracker +requests-oauthlib==1.3.1 \ + --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ + --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a + # via queue-api +rpds-py==0.18.1 \ + --hash=sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc \ + --hash=sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7 \ + --hash=sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7 \ + --hash=sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100 \ + --hash=sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261 \ + --hash=sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8 \ + --hash=sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2 \ + --hash=sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d \ + --hash=sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07 \ + --hash=sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8 \ + --hash=sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88 \ + --hash=sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb \ + --hash=sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f \ + --hash=sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e + # via + # jsonschema + # queue-api + # referencing +rsa==4.9.1 \ + --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ + --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 + # via + # python-jose + # queue-api +setuptools==82.0.1 \ + --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \ + --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb + # via + # flake8-import-order + # sqlalchemy-utc + # zope-event + # zope-interface +simple-websocket==1.0.0 \ + --hash=sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8 \ + --hash=sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc + # via + # python-engineio + # queue-api +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # ecdsa + # flask-jwt-oidc + # python-dateutil +snowplow-tracker==1.1.0 \ + --hash=sha256:24ea32ddac9cca547421bf9ab162f5f33c00711c6ef118ad5f78093cee962224 \ + --hash=sha256:95d8fdc8bd542fd12a0b9a076852239cbaf0599eda8721deaf5f93f7138fe755 + # via queue-api +sqlalchemy==2.0.48 \ + --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ + --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ + --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ + --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ + --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ + --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ + --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ + --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ + --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c + # via + # alembic + # flask-sqlalchemy + # marshmallow-sqlalchemy + # queue-api + # sqlalchemy-utc + # sqlalchemy-utils +sqlalchemy-utc==0.14.0 \ + --hash=sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa \ + --hash=sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b + # via queue-api +sqlalchemy-utils==0.42.1 \ + --hash=sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80 \ + --hash=sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e + # via queue-api +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via queue-api +tomli==2.4.1 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 + # via + # coverage + # zimports +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via pylint +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via + # alembic + # minio + # queue-api + # snowplow-tracker + # sqlalchemy +tzdata==2025.3 \ + --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ + --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 + # via + # kombu + # queue-api +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via + # minio + # queue-api + # requests +vine==5.1.0 \ + --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \ + --hash=sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0 + # via + # amqp + # kombu + # queue-api +werkzeug==3.1.6 \ + --hash=sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25 \ + --hash=sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131 + # via + # flask + # flask-admin + # flask-login + # flask-restx + # flask-socketio + # queue-api +wrapt==1.12.1 \ + --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 + # via queue-api +wsproto==1.2.0 \ + --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ + --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 + # via + # queue-api + # simple-websocket +wtforms==3.2.1 \ + --hash=sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4 \ + --hash=sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682 + # via + # flask-admin + # queue-api +zimports==0.6.3 \ + --hash=sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6 \ + --hash=sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe + # via flask-jwt-oidc +zope-event==5.0 \ + --hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \ + --hash=sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd + # via + # gevent + # queue-api +zope-interface==6.3 \ + --hash=sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130 \ + --hash=sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10 \ + --hash=sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299 \ + --hash=sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39 \ + --hash=sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061 \ + --hash=sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e \ + --hash=sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a + # via + # gevent + # queue-api +zstandard==0.25.0 \ + --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ + --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ + --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ + --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ + --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ + --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ + --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ + --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ + --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ + --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ + --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ + --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ + --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ + --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ + --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ + --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ + --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ + --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 + # via + # flask-compress + # queue-api diff --git a/api/requirements_dev.txt b/api/requirements_dev.txt old mode 100755 new mode 100644 index 47890fc16..96e2a4eac --- a/api/requirements_dev.txt +++ b/api/requirements_dev.txt @@ -1,18 +1,808 @@ -# Copyright 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -# Requirements for dev: everything deployed plus dev tools. - --r requirements.txt -pylint==2.7.2 +# This file was autogenerated by uv via the following command: +# uv export --frozen --cache-dir /tmp/uv-cache --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt +alembic==1.18.4 \ + --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ + --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc + # via + # flask-migrate + # queue-api +amqp==5.2.0 \ + --hash=sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637 \ + --hash=sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd + # via + # kombu + # queue-api +aniso8601==9.0.1 \ + --hash=sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f \ + --hash=sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973 + # via + # flask-restx + # queue-api +argon2-cffi==25.1.0 \ + --hash=sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1 \ + --hash=sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 + # via + # minio + # queue-api +argon2-cffi-bindings==25.1.0 \ + --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ + --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ + --hash=sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44 \ + --hash=sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2 \ + --hash=sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0 \ + --hash=sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98 \ + --hash=sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500 \ + --hash=sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94 \ + --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ + --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ + --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a + # via + # argon2-cffi + # queue-api +astroid==4.0.4 \ + --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ + --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 + # via pylint +async-timeout==5.0.1 ; python_full_version < '3.11.3' \ + --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ + --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 + # via redis +attrs==23.2.0 \ + --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ + --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 + # via + # jsonschema + # queue-api + # referencing +bidict==0.23.1 \ + --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ + --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 + # via + # python-socketio + # queue-api +blinker==1.9.0 \ + --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ + --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc + # via + # flask + # flask-socketio + # queue-api +brotli==1.1.0 \ + --hash=sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9 \ + --hash=sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757 \ + --hash=sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0 \ + --hash=sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd \ + --hash=sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50 \ + --hash=sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265 \ + --hash=sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327 \ + --hash=sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd \ + --hash=sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724 \ + --hash=sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8 \ + --hash=sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc \ + --hash=sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61 \ + --hash=sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1 \ + --hash=sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f \ + --hash=sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6 \ + --hash=sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf \ + --hash=sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b + # via + # flask-compress + # queue-api +brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ + --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ + --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ + --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \ + --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \ + --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \ + --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \ + --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \ + --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \ + --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \ + --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1 + # via flask-compress +cachelib==0.13.0 \ + --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ + --hash=sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516 + # via + # flask-caching + # flask-jwt-oidc + # queue-api +certifi==2024.7.4 \ + --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ + --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 + # via + # minio + # queue-api + # requests +cffi==2.0.0 \ + --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ + --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ + --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ + --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ + --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ + --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ + --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ + --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ + --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ + --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ + --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ + --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d + # via + # argon2-cffi-bindings + # brotlicffi + # cryptography + # gevent + # queue-api +charset-normalizer==3.3.2 \ + --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ + --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ + --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ + --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ + --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ + --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ + --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ + --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ + --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ + --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ + --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ + --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ + --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ + --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ + --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ + --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ + --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 + # via + # queue-api + # requests +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de + # via + # flask + # flask-socketio + # queue-api +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # click + # pylint + # pytest +coverage==7.13.5 \ + --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \ + --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \ + --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \ + --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \ + --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \ + --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ + --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \ + --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \ + --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \ + --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \ + --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \ + --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \ + --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \ + --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \ + --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ + --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ + --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b + # via pytest-cov +cryptography==46.0.5 \ + --hash=sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72 \ + --hash=sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235 \ + --hash=sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356 \ + --hash=sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257 \ + --hash=sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad \ + --hash=sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4 \ + --hash=sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c \ + --hash=sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614 \ + --hash=sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed \ + --hash=sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31 \ + --hash=sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229 \ + --hash=sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0 \ + --hash=sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731 \ + --hash=sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b \ + --hash=sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263 \ + --hash=sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595 \ + --hash=sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1 \ + --hash=sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48 \ + --hash=sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76 \ + --hash=sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18 \ + --hash=sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d \ + --hash=sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1 \ + --hash=sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7 \ + --hash=sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82 \ + --hash=sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4 \ + --hash=sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c \ + --hash=sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d \ + --hash=sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a \ + --hash=sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a \ + --hash=sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d \ + --hash=sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b \ + --hash=sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9 \ + --hash=sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da \ + --hash=sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2 \ + --hash=sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2 + # via + # flask-jwt-oidc + # queue-api +decorator==5.0.9 \ + --hash=sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323 \ + --hash=sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5 + # via queue-api +dill==0.4.1 \ + --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ + --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa + # via + # pylint + # queue-api +ecdsa==0.19.0 \ + --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ + --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 + # via + # python-jose + # queue-api +filelock==3.0.12 \ + --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ + --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 + # via queue-api +flake8-import-order==0.19.2 \ + --hash=sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2 \ + --hash=sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c + # via zimports +flask==3.1.3 \ + --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ + --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c + # via + # flask-admin + # flask-caching + # flask-compress + # flask-cors + # flask-jwt-oidc + # flask-login + # flask-marshmallow + # flask-migrate + # flask-restx + # flask-socketio + # flask-sqlalchemy + # queue-api +flask-admin==2.0.2 \ + --hash=sha256:1d06aec7efee957972b43f6b08a0bd08d5f4cf9a337d4ece2f17c98abc2a214e \ + --hash=sha256:4b3c44068de0fe4630dfcd190cc11231cbbdd7bac315c74c55d1764087b8b273 + # via queue-api +flask-caching==2.3.1 \ + --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ + --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 + # via queue-api +flask-compress==1.17 \ + --hash=sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8 \ + --hash=sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20 + # via queue-api +flask-cors==5.0.0 \ + --hash=sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef \ + --hash=sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc + # via queue-api +flask-jwt-oidc==0.8.0 \ + --hash=sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0 \ + --hash=sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4 + # via queue-api +flask-login==0.6.3 \ + --hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \ + --hash=sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d + # via queue-api +flask-marshmallow==1.4.0 \ + --hash=sha256:98c90a253052c72d2ddddc925539ac33bbd780c6fba86478ffe18e3b89d8b471 \ + --hash=sha256:b758fc2c428d0cbee6fd0ccf0d55524fe9e426a86a177dcc0fc8cd71ad4b7c59 + # via queue-api +flask-migrate==4.1.0 \ + --hash=sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d \ + --hash=sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d + # via queue-api +flask-restx==1.3.2 \ + --hash=sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5 \ + --hash=sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b + # via queue-api +flask-socketio==5.6.1 \ + --hash=sha256:51a3f71b28b4476c650829607e3a993e076034db6c3cc31f718f0a4b45939d42 \ + --hash=sha256:fe5bd995c3ed4da9a98f335d0d830fa1a19d84a64789f6265642a671fdacaeac + # via queue-api +flask-sqlalchemy==3.1.1 \ + --hash=sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0 \ + --hash=sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312 + # via + # flask-migrate + # queue-api +gevent==25.9.1 \ + --hash=sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7 \ + --hash=sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457 \ + --hash=sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff \ + --hash=sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586 \ + --hash=sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235 \ + --hash=sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd \ + --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ + --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 + # via queue-api +greenlet==3.3.2 \ + --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ + --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ + --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ + --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ + --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ + --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ + --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ + --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ + --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ + --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 + # via + # gevent + # queue-api + # sqlalchemy +gunicorn==25.2.0 \ + --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ + --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc + # via queue-api +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via + # queue-api + # wsproto +idna==3.7 \ + --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ + --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 + # via + # queue-api + # requests +ijson==2.6.1 \ + --hash=sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0 + # via queue-api +importlib-resources==6.5.2 \ + --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ + --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec + # via flask-restx +iniconfig==2.3.0 \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + # via pytest +isort==8.0.1 \ + --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ + --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 + # via pylint +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 + # via + # flask + # queue-api +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # flask + # flask-admin + # flask-socketio + # queue-api +jsonschema==4.22.0 \ + --hash=sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7 \ + --hash=sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802 + # via + # flask-restx + # queue-api +jsonschema-specifications==2023.12.1 \ + --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ + --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c + # via + # jsonschema + # queue-api +kombu==5.6.2 \ + --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \ + --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93 + # via queue-api +lazy-object-proxy==1.10.0 \ + --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \ + --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \ + --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \ + --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \ + --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \ + --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \ + --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \ + --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \ + --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c + # via queue-api +mako==1.3.3 \ + --hash=sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40 \ + --hash=sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73 + # via + # alembic + # queue-api +markupsafe==2.1.5 \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b + # via + # flask + # flask-admin + # jinja2 + # mako + # queue-api + # werkzeug + # wtforms +marshmallow==3.21.2 \ + --hash=sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1 \ + --hash=sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56 + # via + # flask-marshmallow + # marshmallow-sqlalchemy + # queue-api +marshmallow-sqlalchemy==1.4.2 \ + --hash=sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c \ + --hash=sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40 + # via queue-api +mccabe==0.6.1 \ + --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ + --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f + # via pylint +minio==7.2.20 \ + --hash=sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598 \ + --hash=sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e + # via queue-api +oauthlib==3.2.2 \ + --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ + --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 + # via + # queue-api + # requests-oauthlib +packaging==24.0 \ + --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ + --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 + # via + # gunicorn + # kombu + # marshmallow + # pytest + # queue-api +platformdirs==4.9.4 \ + --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 + # via + # pylint + # queue-api +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + # via + # pytest + # pytest-cov +psycopg2-binary==2.9.11 \ + --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \ + --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \ + --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \ + --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \ + --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \ + --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \ + --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \ + --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \ + --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \ + --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \ + --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ + --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 + # via queue-api +pyasn1==0.6.0 \ + --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ + --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 + # via + # python-jose + # queue-api + # rsa +pycodestyle==2.14.0 \ + --hash=sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783 \ + --hash=sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d + # via flake8-import-order +pycparser==3.0 \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via + # cffi + # queue-api +pycryptodome==3.23.0 \ + --hash=sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c \ + --hash=sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f \ + --hash=sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27 \ + --hash=sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef \ + --hash=sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886 \ + --hash=sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a \ + --hash=sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490 \ + --hash=sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b \ + --hash=sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2 \ + --hash=sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575 \ + --hash=sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843 \ + --hash=sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa + # via + # minio + # queue-api +pyflakes==3.4.0 \ + --hash=sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58 \ + --hash=sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f + # via zimports +pygments==2.19.2 \ + --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ + --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b + # via + # pytest + # queue-api +pyjwt==2.12.1 \ + --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ + --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b + # via + # flask-jwt-oidc + # queue-api +pylint==4.0.5 \ + --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ + --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c +pyparsing==3.0.7 \ + --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \ + --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484 + # via queue-api +pytest==9.0.2 \ + --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ + --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 + # via pytest-cov +pytest-cov==7.0.0 \ + --hash=sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1 \ + --hash=sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via queue-api +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via queue-api +python-engineio==4.13.1 \ + --hash=sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066 \ + --hash=sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399 + # via + # python-socketio + # queue-api +python-jose==3.3.0 \ + --hash=sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a \ + --hash=sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a + # via queue-api +python-magic==0.4.27 \ + --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ + --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 + # via queue-api +python-socketio==5.16.1 \ + --hash=sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35 \ + --hash=sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89 + # via + # flask-socketio + # queue-api +redis==7.4.0 \ + --hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \ + --hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec + # via queue-api +referencing==0.35.1 \ + --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ + --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de + # via + # flask-restx + # jsonschema + # jsonschema-specifications + # queue-api +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via + # queue-api + # requests-oauthlib + # snowplow-tracker +requests-oauthlib==1.3.1 \ + --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ + --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a + # via queue-api +rpds-py==0.18.1 \ + --hash=sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc \ + --hash=sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7 \ + --hash=sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7 \ + --hash=sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100 \ + --hash=sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261 \ + --hash=sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8 \ + --hash=sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2 \ + --hash=sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d \ + --hash=sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07 \ + --hash=sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8 \ + --hash=sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88 \ + --hash=sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb \ + --hash=sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f \ + --hash=sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e + # via + # jsonschema + # queue-api + # referencing +rsa==4.9.1 \ + --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ + --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 + # via + # python-jose + # queue-api +setuptools==82.0.1 \ + --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \ + --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb + # via + # flake8-import-order + # sqlalchemy-utc + # zope-event + # zope-interface +simple-websocket==1.0.0 \ + --hash=sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8 \ + --hash=sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc + # via + # python-engineio + # queue-api +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # ecdsa + # flask-jwt-oidc + # python-dateutil +snowplow-tracker==1.1.0 \ + --hash=sha256:24ea32ddac9cca547421bf9ab162f5f33c00711c6ef118ad5f78093cee962224 \ + --hash=sha256:95d8fdc8bd542fd12a0b9a076852239cbaf0599eda8721deaf5f93f7138fe755 + # via queue-api +sqlalchemy==2.0.48 \ + --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ + --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ + --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ + --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ + --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ + --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ + --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ + --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ + --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c + # via + # alembic + # flask-sqlalchemy + # marshmallow-sqlalchemy + # queue-api + # sqlalchemy-utc + # sqlalchemy-utils +sqlalchemy-utc==0.14.0 \ + --hash=sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa \ + --hash=sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b + # via queue-api +sqlalchemy-utils==0.42.1 \ + --hash=sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80 \ + --hash=sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e + # via queue-api +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via queue-api +tomli==2.4.1 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 + # via + # coverage + # zimports +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via pylint +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via + # alembic + # minio + # queue-api + # snowplow-tracker + # sqlalchemy +tzdata==2025.3 \ + --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ + --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 + # via + # kombu + # queue-api +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via + # minio + # queue-api + # requests +vine==5.1.0 \ + --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \ + --hash=sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0 + # via + # amqp + # kombu + # queue-api +werkzeug==3.1.6 \ + --hash=sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25 \ + --hash=sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131 + # via + # flask + # flask-admin + # flask-login + # flask-restx + # flask-socketio + # queue-api +wrapt==1.12.1 \ + --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 + # via queue-api +wsproto==1.2.0 \ + --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ + --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 + # via + # queue-api + # simple-websocket +wtforms==3.2.1 \ + --hash=sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4 \ + --hash=sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682 + # via + # flask-admin + # queue-api +zimports==0.6.3 \ + --hash=sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6 \ + --hash=sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe + # via flask-jwt-oidc +zope-event==5.0 \ + --hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \ + --hash=sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd + # via + # gevent + # queue-api +zope-interface==6.3 \ + --hash=sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130 \ + --hash=sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10 \ + --hash=sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299 \ + --hash=sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39 \ + --hash=sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061 \ + --hash=sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e \ + --hash=sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a + # via + # gevent + # queue-api +zstandard==0.25.0 \ + --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ + --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ + --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ + --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ + --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ + --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ + --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ + --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ + --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ + --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ + --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ + --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ + --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ + --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ + --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ + --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ + --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ + --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 + # via + # flask-compress + # queue-api diff --git a/api/scripts/export_requirements.sh b/api/scripts/export_requirements.sh new file mode 100644 index 000000000..699412610 --- /dev/null +++ b/api/scripts/export_requirements.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +api_dir="$(cd "${script_dir}/.." && pwd)" + +cd "${api_dir}" +uv export --frozen --format requirements-txt --no-emit-project --output-file requirements.txt +uv export --frozen --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt diff --git a/api/scripts/run_sqlalchemy_smoke_tests.sh b/api/scripts/run_sqlalchemy_smoke_tests.sh index 692468c90..edf634183 100755 --- a/api/scripts/run_sqlalchemy_smoke_tests.sh +++ b/api/scripts/run_sqlalchemy_smoke_tests.sh @@ -5,4 +5,4 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" api_dir="$(cd "${script_dir}/.." && pwd)" cd "${api_dir}" -python3 -m pytest app/tests -q +uv run pytest app/tests -q diff --git a/api/scripts/run_sqlalchemy_warn20_tests.sh b/api/scripts/run_sqlalchemy_warn20_tests.sh index 20c290843..d4bac87fd 100755 --- a/api/scripts/run_sqlalchemy_warn20_tests.sh +++ b/api/scripts/run_sqlalchemy_warn20_tests.sh @@ -6,6 +6,6 @@ api_dir="$(cd "${script_dir}/.." && pwd)" cd "${api_dir}" export SQLALCHEMY_WARN_20=1 -python3 -m pytest app/tests -q \ +uv run pytest app/tests -q \ -W "error::sqlalchemy.exc.RemovedIn20Warning" \ -W "error::sqlalchemy.exc.SADeprecationWarning" diff --git a/api/setup.cfg b/api/setup.cfg index 580475e0b..d9d7bdc8e 100755 --- a/api/setup.cfg +++ b/api/setup.cfg @@ -1,38 +1,3 @@ -[metadata] -name = queue_api -url = https://github.com/bcgov/queue-management/ -author = Service BC Team -author_email = -classifiers = - Development Status :: Beta - Intended Audience :: Developers / QA - Topic :: Service BC - License :: OSI Approved :: Apache Software License - Natural Language :: English - Programming Language :: Python :: 3.6 -license = Apache Software License Version 2.0 -description = A short description of the project -long_description = file: README.md -keywords = - -[options] -zip_safe = True -python_requires = >=3.6 -include_package_data = True -packages = find: - -[options.package_data] -queue_api = - -[wheel] -universal = 1 - -[bdist_wheel] -universal = 1 - -[aliases] -test = pytest - [flake8] ignore = I001, I003, I004, E126, W504 exclude = .git,*migrations* diff --git a/api/setup.py b/api/setup.py deleted file mode 100755 index c5a322c53..000000000 --- a/api/setup.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright Β© 2019 Province of British Columbia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Installer and setup for this module -""" -from glob import glob -from os.path import basename, splitext - -from setuptools import find_packages, setup - - -def read_requirements(filename): - """ - Get application requirements from - the requirements.txt file. - :return: Python requirements - :rtype: list - """ - with open(filename, 'r') as req: - requirements = req.readlines() - install_requires = [r.strip() for r in requirements if r.find('git+') != 0] - return install_requires - - -def read(filepath): - """ - Read the contents from a file. - :param str filepath: path to the file to be read - :return: file contents - :rtype: str - """ - with open(filepath, 'r') as file_handle: - content = file_handle.read() - return content - - -REQUIREMENTS = read_requirements('requirements.txt') - -setup( - name="queue_api", - packages=find_packages() -) diff --git a/api/uv.lock b/api/uv.lock new file mode 100644 index 000000000..ed344cdaf --- /dev/null +++ b/api/uv.lock @@ -0,0 +1,1632 @@ +version = 1 +revision = 3 +requires-python = "==3.11.*" +resolution-markers = [ + "platform_python_implementation != 'PyPy'", + "platform_python_implementation == 'PyPy'", +] + +[[package]] +name = "alembic" +version = "1.18.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, +] + +[[package]] +name = "amqp" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/2c/6eb09fbdeb3c060b37bd33f8873832897a83e7a428afe01aad333fc405ec/amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd", size = 128754, upload-time = "2023-11-06T04:54:20.099Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/f0/8e5be5d5e0653d9e1d02b1144efa33ff7d2963dfad07049e02c0fa9b2e8d/amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637", size = 50917, upload-time = "2023-11-06T04:54:08.603Z" }, +] + +[[package]] +name = "aniso8601" +version = "9.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973", size = 47345, upload-time = "2021-03-02T01:33:22.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/04/e97c12dc034791d7b504860acfcdd2963fa21ae61eaca1c9d31245f812c3/aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f", size = 52754, upload-time = "2021-03-02T01:33:20.669Z" }, +] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, +] + +[[package]] +name = "astroid" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "23.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820, upload-time = "2023-12-31T06:30:32.926Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752, upload-time = "2023-12-31T06:30:30.772Z" }, +] + +[[package]] +name = "bidict" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093, upload-time = "2024-02-18T19:09:05.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload-time = "2024-02-18T19:09:04.156Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "brotli" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload-time = "2023-09-07T14:03:37.779Z" }, + { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload-time = "2023-09-07T14:03:39.223Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload-time = "2023-09-07T14:03:40.858Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload-time = "2023-09-07T14:03:42.896Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload-time = "2023-09-07T14:03:44.552Z" }, + { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload-time = "2023-09-07T14:03:46.594Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload-time = "2023-09-07T14:03:48.204Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload-time = "2023-09-07T14:03:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload-time = "2023-09-07T14:03:52.395Z" }, + { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload-time = "2023-09-07T14:03:53.96Z" }, + { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload-time = "2024-10-18T12:32:16.688Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload-time = "2024-10-18T12:32:18.459Z" }, + { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload-time = "2024-10-18T12:32:20.192Z" }, + { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload-time = "2024-10-18T12:32:21.774Z" }, + { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload-time = "2023-09-07T14:03:55.404Z" }, + { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload-time = "2023-09-07T14:03:56.643Z" }, +] + +[[package]] +name = "brotlicffi" +version = "1.2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/017dc5f852ed9b8735af77774509271acbf1de02d238377667145fcee01d/brotlicffi-1.2.0.1.tar.gz", hash = "sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c", size = 478156, upload-time = "2026-03-05T19:54:11.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/9f/b98dcd4af47994cee97aebac866996a006a2e5fc1fd1e2b82a8ad95cf09c/brotlicffi-1.2.0.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4", size = 432608, upload-time = "2026-03-05T19:53:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7a/ac4ee56595a061e3718a6d1ea7e921f4df156894acffb28ed88a1fd52022/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce", size = 1534257, upload-time = "2026-03-05T19:53:58.667Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/e7410db7f6f56de57744ea52a115084ceb2735f4d44973f349bb92136586/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a", size = 1536838, upload-time = "2026-03-05T19:54:00.705Z" }, + { url = "https://files.pythonhosted.org/packages/a6/75/6e7977d1935fc3fbb201cbd619be8f2c7aea25d40a096967132854b34708/brotlicffi-1.2.0.1-cp38-abi3-win32.whl", hash = "sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187", size = 343337, upload-time = "2026-03-05T19:54:02.446Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ef/e7e485ce5e4ba3843a0a92feb767c7b6098fd6e65ce752918074d175ae71/brotlicffi-1.2.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede", size = 379026, upload-time = "2026-03-05T19:54:04.322Z" }, + { url = "https://files.pythonhosted.org/packages/7f/53/6262c2256513e6f530d81642477cb19367270922063eaa2d7b781d8c723d/brotlicffi-1.2.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851", size = 402265, upload-time = "2026-03-05T19:54:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d9/d5340b43cf5fbe7fe5a083d237e5338cc1caa73bea523be1c5e452c26290/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf", size = 406710, upload-time = "2026-03-05T19:54:07.272Z" }, + { url = "https://files.pythonhosted.org/packages/a3/82/dbced4c1e0792efdf23fd90ff6d2a320c64ff4dfef7aacc85c04fde9ddd2/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4", size = 402787, upload-time = "2026-03-05T19:54:08.73Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6f/534205ba7590c9a8716a614f270c5c2ec419b5b7079b3f9cd31b7b5580de/brotlicffi-1.2.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1", size = 375108, upload-time = "2026-03-05T19:54:10.079Z" }, +] + +[[package]] +name = "cachelib" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/69/0b5c1259e12fbcf5c2abe5934b5c0c1294ec0f845e2b4b2a51a91d79a4fb/cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48", size = 34418, upload-time = "2024-04-13T14:18:27.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/42/960fc9896ddeb301716fdd554bab7941c35fb90a1dc7260b77df3366f87f/cachelib-0.13.0-py3-none-any.whl", hash = "sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516", size = 20914, upload-time = "2024-04-13T14:18:26.361Z" }, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065, upload-time = "2024-07-04T01:36:11.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960, upload-time = "2024-07-04T01:36:09.038Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647, upload-time = "2023-11-01T04:02:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434, upload-time = "2023-11-01T04:02:57.173Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979, upload-time = "2023-11-01T04:02:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload-time = "2023-11-01T04:02:59.776Z" }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload-time = "2023-11-01T04:03:02.186Z" }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload-time = "2023-11-01T04:03:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload-time = "2023-11-01T04:03:05.983Z" }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload-time = "2023-11-01T04:03:07.567Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload-time = "2023-11-01T04:03:08.886Z" }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload-time = "2023-11-01T04:03:10.613Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload-time = "2023-11-01T04:03:11.973Z" }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload-time = "2023-11-01T04:03:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload-time = "2023-11-01T04:03:17.362Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509, upload-time = "2023-11-01T04:03:21.453Z" }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870, upload-time = "2023-11-01T04:03:22.723Z" }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d", size = 219381, upload-time = "2026-03-17T10:30:14.68Z" }, + { url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587", size = 219880, upload-time = "2026-03-17T10:30:16.231Z" }, + { url = "https://files.pythonhosted.org/packages/55/2f/e0e5b237bffdb5d6c530ce87cc1d413a5b7d7dfd60fb067ad6d254c35c76/coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642", size = 250303, upload-time = "2026-03-17T10:30:17.748Z" }, + { url = "https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b", size = 252218, upload-time = "2026-03-17T10:30:19.804Z" }, + { url = "https://files.pythonhosted.org/packages/da/69/2f47bb6fa1b8d1e3e5d0c4be8ccb4313c63d742476a619418f85740d597b/coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686", size = 254326, upload-time = "2026-03-17T10:30:21.321Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d0/79db81da58965bd29dabc8f4ad2a2af70611a57cba9d1ec006f072f30a54/coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743", size = 256267, upload-time = "2026-03-17T10:30:23.094Z" }, + { url = "https://files.pythonhosted.org/packages/e5/32/d0d7cc8168f91ddab44c0ce4806b969df5f5fdfdbb568eaca2dbc2a04936/coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75", size = 250430, upload-time = "2026-03-17T10:30:25.311Z" }, + { url = "https://files.pythonhosted.org/packages/4d/06/a055311d891ddbe231cd69fdd20ea4be6e3603ffebddf8704b8ca8e10a3c/coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209", size = 252017, upload-time = "2026-03-17T10:30:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f6/d0fd2d21e29a657b5f77a2fe7082e1568158340dceb941954f776dce1b7b/coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a", size = 250080, upload-time = "2026-03-17T10:30:29.481Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ab/0d7fb2efc2e9a5eb7ddcc6e722f834a69b454b7e6e5888c3a8567ecffb31/coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e", size = 253843, upload-time = "2026-03-17T10:30:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/ba/6f/7467b917bbf5408610178f62a49c0ed4377bb16c1657f689cc61470da8ce/coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd", size = 249802, upload-time = "2026-03-17T10:30:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/75/2c/1172fb689df92135f5bfbbd69fc83017a76d24ea2e2f3a1154007e2fb9f8/coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8", size = 250707, upload-time = "2026-03-17T10:30:35.2Z" }, + { url = "https://files.pythonhosted.org/packages/67/21/9ac389377380a07884e3b48ba7a620fcd9dbfaf1d40565facdc6b36ec9ef/coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf", size = 221880, upload-time = "2026-03-17T10:30:36.775Z" }, + { url = "https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9", size = 222816, upload-time = "2026-03-17T10:30:38.891Z" }, + { url = "https://files.pythonhosted.org/packages/12/a6/1d3f6155fb0010ca68eba7fe48ca6c9da7385058b77a95848710ecf189b1/coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028", size = 221483, upload-time = "2026-03-17T10:30:40.463Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, + { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, + { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, + { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, +] + +[[package]] +name = "decorator" +version = "5.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5", size = 34544, upload-time = "2021-05-16T04:08:35.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/36/b1b9bfdf28690ae01d9ca0aa5b0d07cb4448ac65fb91dc7e2d094e3d992f/decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323", size = 8901, upload-time = "2021-05-16T04:08:33.379Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "ecdsa" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/d0/ec8ac1de7accdcf18cfe468653ef00afd2f609faf67c423efbd02491051b/ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8", size = 197791, upload-time = "2024-04-08T19:01:03.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/e7/ed3243b30d1bec41675b6394a1daae46349dc2b855cb83be846a5a918238/ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a", size = 149266, upload-time = "2024-04-08T19:01:00.977Z" }, +] + +[[package]] +name = "filelock" +version = "3.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/ec/6ee2168387ce0154632f856d5cc5592328e9cf93127c5c9aeca92c8c16cb/filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", size = 8549, upload-time = "2019-05-18T18:07:03.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/83/71a2ee6158bb9f39a90c0dea1637f81d5eef866e188e1971a1b1ab01a35a/filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836", size = 7576, upload-time = "2019-05-18T18:07:01.303Z" }, +] + +[[package]] +name = "flake8-import-order" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycodestyle" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/2f/5d2095e2f63b5fabe2d4a7e97c28bfcf901dcd9335650d9e582283bb02b5/flake8_import_order-0.19.2.tar.gz", hash = "sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2", size = 31867, upload-time = "2025-06-24T12:47:39.458Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/d4/3d067f1c4a429e82ec9ae54a346ef50e4d317c6cdfba6bd1443c162ff39f/flake8_import_order-0.19.2-py3-none-any.whl", hash = "sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c", size = 16323, upload-time = "2025-06-24T12:47:38.259Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "flask-admin" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, + { name = "wtforms" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/b7/78a1534b4fe1a40bccf79e8e274e1ace6ee1678c804e4c178b1c96e6c8d6/flask_admin-2.0.2.tar.gz", hash = "sha256:1d06aec7efee957972b43f6b08a0bd08d5f4cf9a337d4ece2f17c98abc2a214e", size = 5528977, upload-time = "2025-11-11T21:59:14.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/25/a379b8dc388630f1d173544c8b3ebbdb2ad5b60992e531b724d7f8a7ea03/flask_admin-2.0.2-py3-none-any.whl", hash = "sha256:4b3c44068de0fe4630dfcd190cc11231cbbdd7bac315c74c55d1764087b8b273", size = 6459099, upload-time = "2025-11-11T21:59:12.82Z" }, +] + +[[package]] +name = "flask-caching" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachelib" }, + { name = "flask" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/80/74846c8af58ed60972d64f23a6cd0c3ac0175677d7555dff9f51bf82c294/flask_caching-2.3.1.tar.gz", hash = "sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9", size = 67560, upload-time = "2025-02-23T01:34:40.207Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/bb/82daa5e2fcecafadcc8659ce5779679d0641666f9252a4d5a2ae987b0506/Flask_Caching-2.3.1-py3-none-any.whl", hash = "sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761", size = 28916, upload-time = "2025-02-23T01:34:37.749Z" }, +] + +[[package]] +name = "flask-compress" +version = "1.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "brotli", marker = "platform_python_implementation != 'PyPy'" }, + { name = "brotlicffi", marker = "platform_python_implementation == 'PyPy'" }, + { name = "flask" }, + { name = "zstandard" }, + { name = "zstandard", marker = "platform_python_implementation == 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/1f/260db5a4517d59bfde7b4a0d71052df68fb84983bda9231100e3b80f5989/flask_compress-1.17.tar.gz", hash = "sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8", size = 15733, upload-time = "2024-10-14T08:13:33.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/54/ff08f947d07c0a8a5d8f1c8e57b142c97748ca912b259db6467ab35983cd/Flask_Compress-1.17-py3-none-any.whl", hash = "sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20", size = 8723, upload-time = "2024-10-14T08:13:31.726Z" }, +] + +[[package]] +name = "flask-cors" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/d0/d9e52b154e603b0faccc0b7c2ad36a764d8755ef4036acbf1582a67fb86b/flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef", size = 30954, upload-time = "2024-08-31T00:44:26.395Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/07/1afa0514c876282bebc1c9aee83c6bb98fe6415cf57b88d9b06e7e29bf9c/Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc", size = 14463, upload-time = "2024-08-31T00:44:24.394Z" }, +] + +[[package]] +name = "flask-jwt-oidc" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachelib" }, + { name = "cryptography" }, + { name = "flask" }, + { name = "pyjwt" }, + { name = "six" }, + { name = "zimports" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/d2/4be4b075b930ab19d537bd8d7cbeeb3492a966b19351c67a9582a6a34438/flask_jwt_oidc-0.8.0.tar.gz", hash = "sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4", size = 7551, upload-time = "2025-01-27T21:58:44.554Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/b3/69b3c09824b9db259ef35cdd757ccd16cc4db31fb98307bb5d138a59f7db/flask_jwt_oidc-0.8.0-py3-none-any.whl", hash = "sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0", size = 9515, upload-time = "2025-01-27T21:58:43.525Z" }, +] + +[[package]] +name = "flask-login" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834, upload-time = "2023-10-30T14:53:21.151Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303, upload-time = "2023-10-30T14:53:19.636Z" }, +] + +[[package]] +name = "flask-marshmallow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "marshmallow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/43/6e5c19e8abc01f5daf1d3c8ad169c495335390572b8bead3f7e7302131c6/flask_marshmallow-1.4.0.tar.gz", hash = "sha256:98c90a253052c72d2ddddc925539ac33bbd780c6fba86478ffe18e3b89d8b471", size = 40970, upload-time = "2026-02-04T16:07:59.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/9b/7d0605c6f90d640547c3c9a0b95bc3bb17e252ae0f46f1dbfa90d2e06518/flask_marshmallow-1.4.0-py3-none-any.whl", hash = "sha256:b758fc2c428d0cbee6fd0ccf0d55524fe9e426a86a177dcc0fc8cd71ad4b7c59", size = 12254, upload-time = "2026-02-04T16:07:58.878Z" }, +] + +[[package]] +name = "flask-migrate" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic" }, + { name = "flask" }, + { name = "flask-sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/47c7b3c93855ceffc2eabfa271782332942443321a07de193e4198f920cf/flask_migrate-4.1.0.tar.gz", hash = "sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d", size = 21965, upload-time = "2025-01-10T18:51:11.848Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/c4/3f329b23d769fe7628a5fc57ad36956f1fb7132cf8837be6da762b197327/Flask_Migrate-4.1.0-py3-none-any.whl", hash = "sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d", size = 21237, upload-time = "2025-01-10T18:51:09.527Z" }, +] + +[[package]] +name = "flask-restx" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aniso8601" }, + { name = "flask" }, + { name = "importlib-resources" }, + { name = "jsonschema" }, + { name = "referencing" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/89/9b9ca58cbb8e9ec46f4a510ba93878e0c88d518bf03c350e3b1b7ad85cbe/flask-restx-1.3.2.tar.gz", hash = "sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5", size = 2814719, upload-time = "2025-09-23T20:34:25.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/3f/b82cd8e733a355db1abb8297afbf59ec972c00ef90bf8d4eed287958b204/flask_restx-1.3.2-py2.py3-none-any.whl", hash = "sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b", size = 2799859, upload-time = "2025-09-23T20:34:23.055Z" }, +] + +[[package]] +name = "flask-socketio" +version = "5.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "flask" }, + { name = "jinja2" }, + { name = "python-socketio" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/61/3287c8a8fe4c3c59f2573d71aea7d334a113383ed3e6eb96e290dc80115f/flask_socketio-5.6.1.tar.gz", hash = "sha256:fe5bd995c3ed4da9a98f335d0d830fa1a19d84a64789f6265642a671fdacaeac", size = 37857, upload-time = "2026-02-21T13:07:52.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/98/2a46f4a3117c17fd36e07ad8b085054451e96723baaeea245682156ba546/flask_socketio-5.6.1-py3-none-any.whl", hash = "sha256:51a3f71b28b4476c650829607e3a993e076034db6c3cc31f718f0a4b45939d42", size = 18683, upload-time = "2026-02-21T13:07:51.442Z" }, +] + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/53/b0a9fcc1b1297f51e68b69ed3b7c3c40d8c45be1391d77ae198712914392/flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312", size = 81899, upload-time = "2023-09-11T21:42:36.147Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/6a/89963a5c6ecf166e8be29e0d1bf6806051ee8fe6c82e232842e3aeac9204/flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", size = 25125, upload-time = "2023-09-11T21:42:34.514Z" }, +] + +[[package]] +name = "gevent" +version = "25.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation == 'CPython' and sys_platform == 'win32'" }, + { name = "greenlet", marker = "platform_python_implementation == 'CPython'" }, + { name = "zope-event" }, + { name = "zope-interface" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/48/b3ef2673ffb940f980966694e40d6d32560f3ffa284ecaeb5ea3a90a6d3f/gevent-25.9.1.tar.gz", hash = "sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd", size = 5059025, upload-time = "2025-09-17T16:15:34.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/86/03f8db0704fed41b0fa830425845f1eb4e20c92efa3f18751ee17809e9c6/gevent-25.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7", size = 1792418, upload-time = "2025-09-17T15:41:24.384Z" }, + { url = "https://files.pythonhosted.org/packages/5f/35/f6b3a31f0849a62cfa2c64574bcc68a781d5499c3195e296e892a121a3cf/gevent-25.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457", size = 1875700, upload-time = "2025-09-17T15:48:59.652Z" }, + { url = "https://files.pythonhosted.org/packages/66/1e/75055950aa9b48f553e061afa9e3728061b5ccecca358cef19166e4ab74a/gevent-25.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235", size = 1831365, upload-time = "2025-09-17T15:49:19.426Z" }, + { url = "https://files.pythonhosted.org/packages/31/e8/5c1f6968e5547e501cfa03dcb0239dff55e44c3660a37ec534e32a0c008f/gevent-25.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a", size = 2122087, upload-time = "2025-09-17T15:15:12.329Z" }, + { url = "https://files.pythonhosted.org/packages/c0/2c/ebc5d38a7542af9fb7657bfe10932a558bb98c8a94e4748e827d3823fced/gevent-25.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff", size = 1808776, upload-time = "2025-09-17T15:52:40.16Z" }, + { url = "https://files.pythonhosted.org/packages/e6/26/e1d7d6c8ffbf76fe1fbb4e77bdb7f47d419206adc391ec40a8ace6ebbbf0/gevent-25.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56", size = 2179141, upload-time = "2025-09-17T15:24:09.895Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6c/bb21fd9c095506aeeaa616579a356aa50935165cc0f1e250e1e0575620a7/gevent-25.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586", size = 1677941, upload-time = "2025-09-17T19:59:50.185Z" }, +] + +[[package]] +name = "greenlet" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, + { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, + { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, + { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, + { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5", size = 230389, upload-time = "2026-02-20T20:17:18.772Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a1/65bbc059a43a7e2143ec4fc1f9e3f673e04f9c7b371a494a101422ac4fd5/greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd", size = 229645, upload-time = "2026-02-20T20:18:18.695Z" }, +] + +[[package]] +name = "gunicorn" +version = "25.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/13/dd3f8e40ea3ee907a6cbf3d1f1f81afcc3ecd0087d313baabfe95372f15c/gunicorn-25.2.0.tar.gz", hash = "sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c", size = 632709, upload-time = "2026-03-24T22:49:54.433Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/53/fb024445837e02cd5cf989cf349bfac6f3f433c05184ea5d49c8ade751c6/gunicorn-25.2.0-py3-none-any.whl", hash = "sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc", size = 211659, upload-time = "2026-03-24T22:49:52.528Z" }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, +] + +[[package]] +name = "idna" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", size = 189575, upload-time = "2024-04-11T03:34:43.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", size = 66836, upload-time = "2024-04-11T03:34:41.447Z" }, +] + +[[package]] +name = "ijson" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/12/3116e1d5752aa9d480eb58ae4b348d38c1aeaf792c5fbca22e44c27d4bf1/ijson-2.6.1.tar.gz", hash = "sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0", size = 29393, upload-time = "2020-02-03T08:24:19.502Z" } + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/f1/1c1dc0f6b3bf9e76f7526562d29c320fa7d6a2f35b37a1392cc0acd58263/jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", size = 325490, upload-time = "2024-04-30T19:44:37.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/2f/324fab4be6fe37fb7b521546e8a557e6cf08c1c1b3d0b4839a00f589d9ef/jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802", size = 88316, upload-time = "2024-04-30T19:44:34.97Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b9/cc0cc592e7c195fb8a650c1d5990b10175cf13b4c97465c72ec841de9e4b/jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", size = 13983, upload-time = "2023-12-25T15:16:53.63Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/07/44bd408781594c4d0a027666ef27fab1e441b109dc3b76b4f836f8fd04fe/jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c", size = 18482, upload-time = "2023-12-25T15:16:51.997Z" }, +] + +[[package]] +name = "kombu" +version = "5.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "amqp" }, + { name = "packaging" }, + { name = "tzdata" }, + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a5/607e533ed6c83ae1a696969b8e1c137dfebd5759a2e9682e26ff1b97740b/kombu-5.6.2.tar.gz", hash = "sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55", size = 472594, upload-time = "2025-12-29T20:30:07.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/0f/834427d8c03ff1d7e867d3db3d176470c64871753252b21b4f4897d1fa45/kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93", size = 214219, upload-time = "2025-12-29T20:30:05.74Z" }, +] + +[[package]] +name = "lazy-object-proxy" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/f0/f02e2d150d581a294efded4020094a371bbab42423fe78625ac18854d89b/lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69", size = 43271, upload-time = "2023-12-15T15:11:41.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/e1/99a7ec68b892c9b8c6212617f54e7e9b0304d47edad8c0ff043ae3aeb1a9/lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c", size = 27434, upload-time = "2023-12-15T15:10:56.157Z" }, + { url = "https://files.pythonhosted.org/packages/1a/76/6a41de4b44d1dcfe4c720d4606de0d7b69b6b450f0bdce16f2e1fb8abc89/lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4", size = 70687, upload-time = "2023-12-15T15:10:57.949Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5d/eaa12126e8989c9bdd21d864cbba2b258cb9ee2f574ada1462a0004cfad8/lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56", size = 69757, upload-time = "2023-12-15T15:10:59.937Z" }, + { url = "https://files.pythonhosted.org/packages/53/a9/6f22cfe9572929656988b72c0de266c5d10755369b575322725f67364c4e/lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9", size = 73709, upload-time = "2023-12-15T15:11:02.161Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/b10fd94710a99a6309f3ad61a4eb480944bbb17fcb41bd2d852fdbee57ee/lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f", size = 73191, upload-time = "2023-12-15T15:11:03.511Z" }, + { url = "https://files.pythonhosted.org/packages/c9/78/a9b9d314da02fe66b632f2354e20e40fc3508befb450b5a17987a222b383/lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03", size = 25773, upload-time = "2023-12-15T15:11:04.781Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/e2d3b0c9efe61f72dc327ce2355941f540e0b0d1f2b3490cbab6bab7d3ea/lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6", size = 27550, upload-time = "2023-12-15T15:11:05.915Z" }, + { url = "https://files.pythonhosted.org/packages/31/8b/94dc8d58704ab87b39faed6f2fc0090b9d90e2e2aa2bbec35c79f3d2a054/lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d", size = 16405, upload-time = "2023-12-15T15:11:40.453Z" }, +] + +[[package]] +name = "mako" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/dc/48e8853daf4b32748d062ce9cd47a744755fb60691ebc211ca689b849c1c/Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73", size = 389980, upload-time = "2024-04-10T15:32:42.69Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/c9/9cd84cbd5816aa8bee5fd5a00f857efd636ec30586848d571b67baf0b868/Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40", size = 78829, upload-time = "2024-04-10T15:32:47.482Z" }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384, upload-time = "2024-02-02T16:31:22.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219, upload-time = "2024-02-02T16:30:19.988Z" }, + { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098, upload-time = "2024-02-02T16:30:21.063Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014, upload-time = "2024-02-02T16:30:22.926Z" }, + { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220, upload-time = "2024-02-02T16:30:24.76Z" }, + { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756, upload-time = "2024-02-02T16:30:25.877Z" }, + { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988, upload-time = "2024-02-02T16:30:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718, upload-time = "2024-02-02T16:30:28.111Z" }, + { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317, upload-time = "2024-02-02T16:30:29.214Z" }, + { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670, upload-time = "2024-02-02T16:30:30.915Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224, upload-time = "2024-02-02T16:30:32.09Z" }, +] + +[[package]] +name = "marshmallow" +version = "3.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/16/06ad266adc423f9d7ee49dce26787b973907aa70213760c9fe1711745405/marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56", size = 176330, upload-time = "2024-05-01T21:10:53.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/24/cbb242420021a79c87768dcd22ce028f48ef40913239ad6106c8a557f52c/marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1", size = 49299, upload-time = "2024-05-01T21:10:51.063Z" }, +] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/8c/861ed468b99773866d59e315a73a02538f503c99167d24b3b3f1c8e0242c/marshmallow_sqlalchemy-1.4.2.tar.gz", hash = "sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c", size = 51428, upload-time = "2025-04-09T23:44:54.734Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/62/9d87301c861b9bded849082d5c5d306dcfd0c3c304b7ed70d2151caaa4da/marshmallow_sqlalchemy-1.4.2-py3-none-any.whl", hash = "sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40", size = 16740, upload-time = "2025-04-09T23:44:52.999Z" }, +] + +[[package]] +name = "mccabe" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/18/fa675aa501e11d6d6ca0ae73a101b2f3571a565e0f7d38e062eec18a91ee/mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f", size = 8612, upload-time = "2017-01-26T22:13:15.699Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/89/479dc97e18549e21354893e4ee4ef36db1d237534982482c3681ee6e7b57/mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", size = 8556, upload-time = "2017-01-26T22:13:14.36Z" }, +] + +[[package]] +name = "minio" +version = "7.2.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi" }, + { name = "certifi" }, + { name = "pycryptodome" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/df/6dfc6540f96a74125a11653cce717603fd5b7d0001a8e847b3e54e72d238/minio-7.2.20.tar.gz", hash = "sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598", size = 136113, upload-time = "2025-11-27T00:37:15.569Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl", hash = "sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e", size = 93751, upload-time = "2025-11-27T00:37:13.993Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352, upload-time = "2022-10-17T20:04:27.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" }, +] + +[[package]] +name = "packaging" +version = "24.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", size = 147882, upload-time = "2024-03-10T09:39:28.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", size = 53488, upload-time = "2024-03-10T09:39:25.947Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/8767aaa597ba424643dc87348c6f1754dd9f48e80fdc1b9f7ca5c3a7c213/psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c", size = 379620, upload-time = "2025-10-10T11:14:48.041Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/ae/8d8266f6dd183ab4d48b95b9674034e1b482a3f8619b33a0d86438694577/psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10", size = 3756452, upload-time = "2025-10-10T11:11:11.583Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/aa03d327739c1be70e09d01182619aca8ebab5970cd0cfa50dd8b9cec2ac/psycopg2_binary-2.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a", size = 3863957, upload-time = "2025-10-10T11:11:16.932Z" }, + { url = "https://files.pythonhosted.org/packages/48/89/3fdb5902bdab8868bbedc1c6e6023a4e08112ceac5db97fc2012060e0c9a/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4", size = 4410955, upload-time = "2025-10-10T11:11:21.21Z" }, + { url = "https://files.pythonhosted.org/packages/ce/24/e18339c407a13c72b336e0d9013fbbbde77b6fd13e853979019a1269519c/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7", size = 4468007, upload-time = "2025-10-10T11:11:24.831Z" }, + { url = "https://files.pythonhosted.org/packages/91/7e/b8441e831a0f16c159b5381698f9f7f7ed54b77d57bc9c5f99144cc78232/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee", size = 4165012, upload-time = "2025-10-10T11:11:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/0d/61/4aa89eeb6d751f05178a13da95516c036e27468c5d4d2509bb1e15341c81/psycopg2_binary-2.9.11-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb", size = 3981881, upload-time = "2025-10-30T02:55:07.332Z" }, + { url = "https://files.pythonhosted.org/packages/76/a1/2f5841cae4c635a9459fe7aca8ed771336e9383b6429e05c01267b0774cf/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f", size = 3650985, upload-time = "2025-10-10T11:11:34.975Z" }, + { url = "https://files.pythonhosted.org/packages/84/74/4defcac9d002bca5709951b975173c8c2fa968e1a95dc713f61b3a8d3b6a/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94", size = 3296039, upload-time = "2025-10-10T11:11:40.432Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c2/782a3c64403d8ce35b5c50e1b684412cf94f171dc18111be8c976abd2de1/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f", size = 3043477, upload-time = "2025-10-30T02:55:11.182Z" }, + { url = "https://files.pythonhosted.org/packages/c8/31/36a1d8e702aa35c38fc117c2b8be3f182613faa25d794b8aeaab948d4c03/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908", size = 3345842, upload-time = "2025-10-10T11:11:45.366Z" }, + { url = "https://files.pythonhosted.org/packages/6e/b4/a5375cda5b54cb95ee9b836930fea30ae5a8f14aa97da7821722323d979b/psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03", size = 2713894, upload-time = "2025-10-10T11:11:48.775Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/a3/d2157f333900747f20984553aca98008b6dc843eb62f3a36030140ccec0d/pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", size = 148088, upload-time = "2024-03-26T20:07:35.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/7e/5f50d07d5e70a2addbccd90ac2950f81d1edd0783630651d9268d7f1db49/pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473", size = 85313, upload-time = "2024-03-26T20:07:32.491Z" }, +] + +[[package]] +name = "pycodestyle" +version = "2.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, +] + +[[package]] +name = "pyflakes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[[package]] +name = "pylint" +version = "4.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/b6/74d9a8a68b8067efce8d07707fe6a236324ee1e7808d2eb3646ec8517c7d/pylint-4.0.5.tar.gz", hash = "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c", size = 1572474, upload-time = "2026-02-20T09:07:33.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.0.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/60/9bed18f43275b34198eb9720d4c1238c68b3755620d20df0afd89424d32b/pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea", size = 884709, upload-time = "2022-01-21T05:41:34.625Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/c1/23fd82ad3121656b585351aba6c19761926bb0db2ebed9e4ff09a43a3fcc/pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484", size = 98049, upload-time = "2022-01-21T05:41:33.032Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-engineio" +version = "4.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "simple-websocket" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/12/bdef9dbeedbe2cdeba2a2056ad27b1fb081557d34b69a97f574843462cae/python_engineio-4.13.1.tar.gz", hash = "sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066", size = 92348, upload-time = "2026-02-06T23:38:06.12Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl", hash = "sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399", size = 59847, upload-time = "2026-02-06T23:38:04.861Z" }, +] + +[[package]] +name = "python-jose" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ecdsa" }, + { name = "pyasn1" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/19/b2c86504116dc5f0635d29f802da858404d77d930a25633d2e86a64a35b3/python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a", size = 129068, upload-time = "2021-06-05T03:30:40.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/2d/e94b2f7bab6773c70efc70a61d66e312e1febccd9e0db6b9e0adf58cbad1/python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a", size = 33530, upload-time = "2021-06-05T03:30:38.099Z" }, +] + +[[package]] +name = "python-magic" +version = "0.4.27" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, +] + +[[package]] +name = "python-socketio" +version = "5.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bidict" }, + { name = "python-engineio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/81/cf8284f45e32efa18d3848ed82cdd4dcc1b657b082458fbe01ad3e1f2f8d/python_socketio-5.16.1.tar.gz", hash = "sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89", size = 128508, upload-time = "2026-02-06T23:42:07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl", hash = "sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35", size = 82054, upload-time = "2026-02-06T23:42:05.772Z" }, +] + +[[package]] +name = "queue-api" +version = "0.0.0" +source = { editable = "." } +dependencies = [ + { name = "alembic" }, + { name = "amqp" }, + { name = "aniso8601" }, + { name = "argon2-cffi" }, + { name = "argon2-cffi-bindings" }, + { name = "attrs" }, + { name = "bidict" }, + { name = "blinker" }, + { name = "brotli" }, + { name = "cachelib" }, + { name = "certifi" }, + { name = "cffi" }, + { name = "charset-normalizer" }, + { name = "click" }, + { name = "cryptography" }, + { name = "decorator" }, + { name = "dill" }, + { name = "ecdsa" }, + { name = "filelock" }, + { name = "flask" }, + { name = "flask-admin" }, + { name = "flask-caching" }, + { name = "flask-compress" }, + { name = "flask-cors" }, + { name = "flask-jwt-oidc" }, + { name = "flask-login" }, + { name = "flask-marshmallow" }, + { name = "flask-migrate" }, + { name = "flask-restx" }, + { name = "flask-socketio" }, + { name = "flask-sqlalchemy" }, + { name = "gevent" }, + { name = "greenlet" }, + { name = "gunicorn" }, + { name = "h11" }, + { name = "idna" }, + { name = "ijson" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "jsonschema-specifications" }, + { name = "kombu" }, + { name = "lazy-object-proxy" }, + { name = "mako" }, + { name = "markupsafe" }, + { name = "marshmallow" }, + { name = "marshmallow-sqlalchemy" }, + { name = "minio" }, + { name = "oauthlib" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "psycopg2-binary" }, + { name = "pyasn1" }, + { name = "pycparser" }, + { name = "pycryptodome" }, + { name = "pygments" }, + { name = "pyjwt" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, + { name = "python-dotenv" }, + { name = "python-engineio" }, + { name = "python-jose" }, + { name = "python-magic" }, + { name = "python-socketio" }, + { name = "redis" }, + { name = "referencing" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "rpds-py" }, + { name = "rsa" }, + { name = "simple-websocket" }, + { name = "snowplow-tracker" }, + { name = "sqlalchemy" }, + { name = "sqlalchemy-utc" }, + { name = "sqlalchemy-utils" }, + { name = "toml" }, + { name = "typing-extensions" }, + { name = "tzdata" }, + { name = "urllib3" }, + { name = "vine" }, + { name = "werkzeug" }, + { name = "wrapt" }, + { name = "wsproto" }, + { name = "wtforms" }, + { name = "zope-event" }, + { name = "zope-interface" }, + { name = "zstandard" }, +] + +[package.dev-dependencies] +dev = [ + { name = "astroid" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "pluggy" }, + { name = "pylint" }, + { name = "pytest" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "alembic", specifier = "==1.18.4" }, + { name = "amqp", specifier = "==5.2.0" }, + { name = "aniso8601", specifier = "==9.0.1" }, + { name = "argon2-cffi", specifier = "==25.1.0" }, + { name = "argon2-cffi-bindings", specifier = "==25.1.0" }, + { name = "attrs", specifier = "==23.2.0" }, + { name = "bidict", specifier = "==0.23.1" }, + { name = "blinker", specifier = "==1.9.0" }, + { name = "brotli", specifier = "==1.1.0" }, + { name = "cachelib", specifier = "==0.13.0" }, + { name = "certifi", specifier = "==2024.7.4" }, + { name = "cffi", specifier = "==2.0.0" }, + { name = "charset-normalizer", specifier = "==3.3.2" }, + { name = "click", specifier = "==8.1.7" }, + { name = "cryptography", specifier = "==46.0.5" }, + { name = "decorator", specifier = "==5.0.9" }, + { name = "dill", specifier = "==0.4.1" }, + { name = "ecdsa", specifier = "==0.19.0" }, + { name = "filelock", specifier = "==3.0.12" }, + { name = "flask", specifier = "==3.1.3" }, + { name = "flask-admin", specifier = "==2.0.2" }, + { name = "flask-caching", specifier = "==2.3.1" }, + { name = "flask-compress", specifier = "==1.17" }, + { name = "flask-cors", specifier = "==5.0.0" }, + { name = "flask-jwt-oidc", specifier = "==0.8.0" }, + { name = "flask-login", specifier = "==0.6.3" }, + { name = "flask-marshmallow", specifier = "==1.4.0" }, + { name = "flask-migrate", specifier = "==4.1.0" }, + { name = "flask-restx", specifier = "==1.3.2" }, + { name = "flask-socketio", specifier = "==5.6.1" }, + { name = "flask-sqlalchemy", specifier = "==3.1.1" }, + { name = "gevent", specifier = "==25.9.1" }, + { name = "greenlet", specifier = "==3.3.2" }, + { name = "gunicorn", specifier = "==25.2.0" }, + { name = "h11", specifier = "==0.14.0" }, + { name = "idna", specifier = "==3.7" }, + { name = "ijson", specifier = "==2.6.1" }, + { name = "itsdangerous", specifier = "==2.2.0" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "jsonschema", specifier = "==4.22.0" }, + { name = "jsonschema-specifications", specifier = "==2023.12.1" }, + { name = "kombu", specifier = "==5.6.2" }, + { name = "lazy-object-proxy", specifier = "==1.10.0" }, + { name = "mako", specifier = "==1.3.3" }, + { name = "markupsafe", specifier = "==2.1.5" }, + { name = "marshmallow", specifier = "==3.21.2" }, + { name = "marshmallow-sqlalchemy", specifier = "==1.4.2" }, + { name = "minio", specifier = "==7.2.20" }, + { name = "oauthlib", specifier = "==3.2.2" }, + { name = "packaging", specifier = "==24.0" }, + { name = "platformdirs", specifier = "==4.9.4" }, + { name = "psycopg2-binary", specifier = "==2.9.11" }, + { name = "pyasn1", specifier = "==0.6.0" }, + { name = "pycparser", specifier = "==3.0" }, + { name = "pycryptodome", specifier = "==3.23.0" }, + { name = "pygments", specifier = "==2.19.2" }, + { name = "pyjwt", specifier = "==2.12.1" }, + { name = "pyparsing", specifier = "==3.0.7" }, + { name = "python-dateutil", specifier = "==2.9.0.post0" }, + { name = "python-dotenv", specifier = "==1.2.2" }, + { name = "python-engineio", specifier = "==4.13.1" }, + { name = "python-jose", specifier = "==3.3.0" }, + { name = "python-magic", specifier = "==0.4.27" }, + { name = "python-socketio", specifier = "==5.16.1" }, + { name = "redis", specifier = "==7.4.0" }, + { name = "referencing", specifier = "==0.35.1" }, + { name = "requests", specifier = "==2.32.5" }, + { name = "requests-oauthlib", specifier = "==1.3.1" }, + { name = "rpds-py", specifier = "==0.18.1" }, + { name = "rsa", specifier = "==4.9.1" }, + { name = "simple-websocket", specifier = "==1.0.0" }, + { name = "snowplow-tracker", specifier = "==1.1.0" }, + { name = "sqlalchemy", specifier = "==2.0.48" }, + { name = "sqlalchemy-utc", specifier = "==0.14.0" }, + { name = "sqlalchemy-utils", specifier = "==0.42.1" }, + { name = "toml", specifier = "==0.10.2" }, + { name = "typing-extensions", specifier = "==4.15.0" }, + { name = "tzdata", specifier = "==2025.3" }, + { name = "urllib3", specifier = "==2.6.3" }, + { name = "vine", specifier = "==5.1.0" }, + { name = "werkzeug", specifier = "==3.1.6" }, + { name = "wrapt", specifier = "==1.12.1" }, + { name = "wsproto", specifier = "==1.2.0" }, + { name = "wtforms", specifier = "==3.2.1" }, + { name = "zope-event", specifier = "==5.0" }, + { name = "zope-interface", specifier = "==6.3" }, + { name = "zstandard", specifier = "==0.25.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "astroid", specifier = "==4.0.4" }, + { name = "isort", specifier = "==8.0.1" }, + { name = "mccabe", specifier = "==0.6.1" }, + { name = "pluggy", specifier = "==1.6.0" }, + { name = "pylint", specifier = "==4.0.5" }, + { name = "pytest", specifier = "==9.0.2" }, + { name = "pytest-cov", specifier = "==7.0.0" }, +] + +[[package]] +name = "redis" +version = "7.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, +] + +[[package]] +name = "referencing" +version = "0.35.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991, upload-time = "2024-05-01T20:26:04.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684, upload-time = "2024-05-01T20:26:02.078Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/52/531ef197b426646f26b53815a7d2a67cb7a331ef098bb276db26a68ac49f/requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a", size = 52027, upload-time = "2022-01-29T18:52:24.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/bb/5deac77a9af870143c684ab46a7934038a53eb4aa975bc0687ed6ca2c610/requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", size = 23892, upload-time = "2022-01-29T18:52:22.279Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/aa/e7c404bdee1db7be09860dff423d022ffdce9269ec8e6532cce09ee7beea/rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", size = 25388, upload-time = "2024-05-06T13:28:34.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/f3/454ef9c66219ea511991e024f3a379fca618acd4cbe12e369b2d02f9d0b6/rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", size = 327805, upload-time = "2024-05-06T13:25:02.669Z" }, + { url = "https://files.pythonhosted.org/packages/58/e3/b5eb611e2d51688726533bb97b420d36a55d4560c53d016e977ff6d48116/rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", size = 322329, upload-time = "2024-05-06T13:25:05.666Z" }, + { url = "https://files.pythonhosted.org/packages/92/48/32bed868dd4e7d16e26457cc0b8634d6b16cb0e082a93f044ef5f7c77361/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", size = 1114289, upload-time = "2024-05-06T13:25:08.806Z" }, + { url = "https://files.pythonhosted.org/packages/77/66/905aa687ea072d8980f7b14eb9e3c3023f5f3892eb8b88be024094956014/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", size = 1123667, upload-time = "2024-05-06T13:25:11.624Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6c/c658183fc2d2a6ed97b0816ab4fef59d609e596bf6f5f0898ae955c14885/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", size = 1145398, upload-time = "2024-05-06T13:25:14.607Z" }, + { url = "https://files.pythonhosted.org/packages/91/33/b680feac0159b5b66ebb9e6d635d78e94486b0b7ed58bea273be2cc80817/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", size = 1309726, upload-time = "2024-05-06T13:25:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a0/a3702128743ae5bf14175a7333a4741db167f62d42f70e0edc15d9cd45c5/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", size = 1114522, upload-time = "2024-05-06T13:25:19.815Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/a4ed8b067a8f55df92dc1bc4d20858f02ddfdba3057f96759f4dd1bff7af/rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", size = 1139472, upload-time = "2024-05-06T13:25:22.14Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ba/adb81247a2fe211ff74cd4c2790454bbf37eb7430ee898e4aa7ce5cb6507/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", size = 1277148, upload-time = "2024-05-06T13:25:25.122Z" }, + { url = "https://files.pythonhosted.org/packages/52/fc/1eb8dcf82ec8d1252c060644fa44478660e94637ddd91dc8d452b467f359/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", size = 1304556, upload-time = "2024-05-06T13:25:27.737Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3f/0b8e2ac89076fede032aae3fc9cf9390c6fd99a470b238fb62bbc64f4cbf/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", size = 1283410, upload-time = "2024-05-06T13:25:29.843Z" }, + { url = "https://files.pythonhosted.org/packages/74/e0/f9dc97509b3048ddc3ab7b0cd48bd25f78dff45bec463c62b0171c57398c/rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb", size = 196622, upload-time = "2024-05-06T13:25:31.794Z" }, + { url = "https://files.pythonhosted.org/packages/ff/26/0778cc18815f20e37eb492bfed622d01722db38b2f3f86790753b01b2a73/rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2", size = 209016, upload-time = "2024-05-06T13:25:33.646Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "simple-websocket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/82/3cf87d317911864a2f2a8daf1779fc7f82d5d55e6a8aaa0315f8209047a7/simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8", size = 13071, upload-time = "2023-10-05T16:28:30.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/ea/288a8ac1d9551354488ff60c0ac6a76acc3b6b60f0460ac1944c75e240da/simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc", size = 13712, upload-time = "2023-10-05T16:28:28.761Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "snowplow-tracker" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/77/1ab6e5bafb9c80d8128f065a355377a04ac5b3c38eb719d920a9909d346e/snowplow_tracker-1.1.0.tar.gz", hash = "sha256:95d8fdc8bd542fd12a0b9a076852239cbaf0599eda8721deaf5f93f7138fe755", size = 34135, upload-time = "2025-02-21T10:58:48.112Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/10/1c76269cbf2d6e127f4415044d9ddb0295858230678bbf4bfba905593c82/snowplow_tracker-1.1.0-py3-none-any.whl", hash = "sha256:24ea32ddac9cca547421bf9ab162f5f33c00711c6ef118ad5f78093cee962224", size = 44128, upload-time = "2025-02-21T10:58:45.818Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc", size = 2157184, upload-time = "2026-03-02T15:38:28.161Z" }, + { url = "https://files.pythonhosted.org/packages/21/4b/4f3d4a43743ab58b95b9ddf5580a265b593d017693df9e08bd55780af5bb/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c", size = 3313555, upload-time = "2026-03-02T15:58:57.21Z" }, + { url = "https://files.pythonhosted.org/packages/21/dd/3b7c53f1dbbf736fd27041aee68f8ac52226b610f914085b1652c2323442/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7", size = 3313057, upload-time = "2026-03-02T15:52:29.366Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cc/3e600a90ae64047f33313d7d32e5ad025417f09d2ded487e8284b5e21a15/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d", size = 3265431, upload-time = "2026-03-02T15:58:59.096Z" }, + { url = "https://files.pythonhosted.org/packages/8b/19/780138dacfe3f5024f4cf96e4005e91edf6653d53d3673be4844578faf1d/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571", size = 3287646, upload-time = "2026-03-02T15:52:31.569Z" }, + { url = "https://files.pythonhosted.org/packages/40/fd/f32ced124f01a23151f4777e4c705f3a470adc7bd241d9f36a7c941a33bf/sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617", size = 2116956, upload-time = "2026-03-02T15:46:54.535Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c", size = 2141627, upload-time = "2026-03-02T15:46:55.849Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, +] + +[[package]] +name = "sqlalchemy-utc" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/db/2d8f421cece2758954278bd16e05e3f73ce86c0062577a1ccfd3eb4208cd/SQLAlchemy-Utc-0.14.0.tar.gz", hash = "sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa", size = 5353, upload-time = "2021-09-24T04:37:58.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/89/ccfe4b579784f852c64f0fcb127e560990c2d75a2514dda51a9c87fb3782/SQLAlchemy_Utc-0.14.0-py2.py3-none-any.whl", hash = "sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b", size = 5979, upload-time = "2021-09-24T04:37:56.555Z" }, +] + +[[package]] +name = "sqlalchemy-utils" +version = "0.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/7d/eb9565b6a49426552a5bf5c57e7c239c506dc0e4e5315aec6d1e8241dc7c/sqlalchemy_utils-0.42.1.tar.gz", hash = "sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e", size = 130789, upload-time = "2025-12-13T03:14:13.591Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/25/7400c18c3ee97914cc99c90007795c00a4ec5b60c853b49db7ba24d11179/sqlalchemy_utils-0.42.1-py3-none-any.whl", hash = "sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80", size = 91761, upload-time = "2025-12-13T03:14:15.014Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "vine" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980, upload-time = "2023-11-05T08:46:53.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636, upload-time = "2023-11-05T08:46:51.205Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, +] + +[[package]] +name = "wrapt" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/f7/e43cefbe88c5fd371f4cf0cf5eb3feccd07515af9fd6cf7dbf1d1793a797/wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7", size = 27488, upload-time = "2020-03-09T02:32:04.07Z" } + +[[package]] +name = "wsproto" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload-time = "2022-08-23T19:58:21.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, +] + +[[package]] +name = "wtforms" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/e4/633d080897e769ed5712dcfad626e55dbd6cf45db0ff4d9884315c6a82da/wtforms-3.2.1.tar.gz", hash = "sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682", size = 137801, upload-time = "2024-10-21T11:34:00.108Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/c9/2088fb5645cd289c99ebe0d4cdcc723922a1d8e1beaefb0f6f76dff9b21c/wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4", size = 152454, upload-time = "2024-10-21T11:33:58.44Z" }, +] + +[[package]] +name = "zimports" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flake8-import-order" }, + { name = "pyflakes" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/36/5ecb5c9bc1eaf743c1526bcca51c4c58a9f7f67440328cd9a6cd2437b01a/zimports-0.6.3.tar.gz", hash = "sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6", size = 17970, upload-time = "2025-11-04T15:23:37.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/9e/1222fda922f7cd7bba2e754998fe1c30e7b19b9b833c61d29a8900ab42de/zimports-0.6.3-py3-none-any.whl", hash = "sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe", size = 20411, upload-time = "2025-11-04T15:23:36.257Z" }, +] + +[[package]] +name = "zope-event" +version = "5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350, upload-time = "2023-06-23T06:28:35.709Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824, upload-time = "2023-06-23T06:28:32.652Z" }, +] + +[[package]] +name = "zope-interface" +version = "6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/bd/a30bf6df24480017171da4f52ee527a72c7a6450c86355011e0156e71723/zope.interface-6.3.tar.gz", hash = "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a", size = 294679, upload-time = "2024-04-12T15:14:21.416Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ff/37b37e408908f6d949cea8b01969204fc76dd0b85eabc41ff8ca3306a940/zope.interface-6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39", size = 202694, upload-time = "2024-04-12T15:14:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3d/b5562aa226faec1705705bd57fc98e87eb857ab20efa0772734b88bd2fce/zope.interface-6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299", size = 202786, upload-time = "2024-04-12T15:14:29.356Z" }, + { url = "https://files.pythonhosted.org/packages/35/0c/2442f1d7fd3fc2f4179892c161f79e7d3dd5dd483c79a57c3f6355675374/zope.interface-6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130", size = 249832, upload-time = "2024-04-12T15:33:08.616Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/79c9014351824b13db0c37808333816401e87d473d268f137de172bacf5a/zope.interface-6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10", size = 243984, upload-time = "2024-04-12T15:14:04.123Z" }, + { url = "https://files.pythonhosted.org/packages/95/00/1c97f0b1622b4eb587e8aea84f109b426c6d3506fba043052dafeb4cab95/zope.interface-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e", size = 249354, upload-time = "2024-04-12T15:14:43.49Z" }, + { url = "https://files.pythonhosted.org/packages/44/25/f993b704a15e75da08ed8ee8cb3cfdb61eb9ccc5d68e0db887e3961520e9/zope.interface-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061", size = 204423, upload-time = "2024-04-12T15:21:41.029Z" }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, +] From 77c433736db582630d8d209f64872d5dced97bdb Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 17:58:37 -0700 Subject: [PATCH 15/81] Fix query error when finishing citizen service --- .../theq/citizen/citizen_finish_service.py | 29 +++++++++++++++---- api/app/tests/test_citizen_finish_service.py | 9 ++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 api/app/tests/test_citizen_finish_service.py diff --git a/api/app/resources/theq/citizen/citizen_finish_service.py b/api/app/resources/theq/citizen/citizen_finish_service.py index 6b4004ace..d7162f32d 100644 --- a/api/app/resources/theq/citizen/citizen_finish_service.py +++ b/api/app/resources/theq/citizen/citizen_finish_service.py @@ -27,6 +27,29 @@ from sqlalchemy.dialects import postgresql +def _citizen_finish_service_query(citizen_id): + return Citizen.query \ + .options( + joinedload(Citizen.service_reqs).options( + joinedload(ServiceReq.periods).options( + joinedload(Period.ps).options(raiseload('*')), + joinedload(Period.csr).options(raiseload('*')), + raiseload('*') + ), + joinedload(ServiceReq.service).options( + joinedload(Service.parent).options( + raiseload(Service.parent).options(raiseload('*')) + ), + raiseload('*') + ) + ), + raiseload(Citizen.user), + joinedload(Citizen.counter), + joinedload(Citizen.office).options(joinedload(Office.sb), raiseload('*')) + ) \ + .filter_by(citizen_id=citizen_id) + + @api.route("/citizens//finish_service/", methods=["POST"]) class CitizenFinishService(Resource): @@ -37,11 +60,7 @@ class CitizenFinishService(Resource): @api_call_with_retry def post(self, id): csr = CSR.find_by_username(get_username()) - citizen = Citizen.query\ - .options(joinedload(Citizen.service_reqs).options(joinedload(ServiceReq.periods).options(joinedload(Period.ps).options(raiseload('*')),joinedload(Period.csr).options(raiseload('*')),raiseload('*')), joinedload(ServiceReq.service).options(joinedload(Service.parent).options(raiseload(Service.parent).options(raiseload('*'))),raiseload('*'))), raiseload(Citizen.counter),raiseload(Citizen.user), joinedload(Citizen.counter), joinedload(Citizen.office).options(joinedload(Office.sb),raiseload('*'))) \ - .filter_by(citizen_id=id) - - citizen = citizen.first() + citizen = _citizen_finish_service_query(id).first() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/finish_service/, Ticket: ' + citizen.ticket_number) active_service_request = citizen.get_active_service_request() diff --git a/api/app/tests/test_citizen_finish_service.py b/api/app/tests/test_citizen_finish_service.py new file mode 100644 index 000000000..045cd439f --- /dev/null +++ b/api/app/tests/test_citizen_finish_service.py @@ -0,0 +1,9 @@ +def test_finish_service_query_compiles_without_loader_conflicts(app, db, migrated_database): + del migrated_database + + from app.resources.theq.citizen.citizen_finish_service import _citizen_finish_service_query + + with app.app_context(): + compiled = _citizen_finish_service_query(1).statement.compile(dialect=db.engine.dialect) + + assert "FROM citizen" in str(compiled) From 711b78f3a116c07d47b0c9540a9d650f0f10489a Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 18:33:13 -0700 Subject: [PATCH 16/81] Update vscode debug --- .vscode/launch.json | 22 ++++++++++------------ .vscode/tasks.json | 17 ++++++++++++++++- api/wsgi.py | 27 +++++++++++++++++++++++++-- documentation/Readme.md | 22 ++++++++++++++++++++-- 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7c8e5c52c..050826c84 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -52,23 +52,21 @@ "type": "node" }, { - // Run the Queue Management API Python gunicorn process. - "args": [ - "wsgi", - "--bind=0.0.0.0:5000", - "--access-logfile=-", - "--config=gunicorn_config.py", - "--reload", - "--timeout=0" // Disable timeout; don't die at breakpoints - ], + // Run the Queue Management API in a single process for reliable debugging. "cwd": "${workspaceFolder}/api", + "env": { + "WSGI_DEBUG": "1", + "WSGI_HOST": "0.0.0.0", + "WSGI_PORT": "5000", + "WSGI_USE_RELOADER": "0" + }, "gevent": true, "name": "queue_management_api", "preLaunchTask": "alembic: upgrade - api", - "program": "env/bin/gunicorn", - "python": "${workspaceFolder}/api/env/bin/python", + "program": "${workspaceFolder}/api/wsgi.py", + "python": "${workspaceFolder}/api/.venv/bin/python", "request": "launch", - "type": "python" + "type": "debugpy" }, { // Run the Vue.js front end for the Queue Management application. diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 07c083ef8..f18a54d31 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,7 +3,7 @@ { "args": [ "-c", - "cd ${workspaceFolder}/api; source env/bin/activate; python manage.py db upgrade" + "cd ${workspaceFolder}/api; source .venv/bin/activate; python manage.py db upgrade" ], "command": "bash", "detail": "python manage.py db upgrade", @@ -15,6 +15,21 @@ "reveal": "always", }, "type": "shell" + }, + { + "args": [ + "-c", + "cd ${workspaceFolder}/api; uv run gunicorn wsgi --bind=0.0.0.0:5000 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0" + ], + "command": "bash", + "detail": "uv run gunicorn wsgi --bind=0.0.0.0:5000 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0", + "label": "api: gunicorn hot reload", + "presentation": { + "close": false, + "panel": "new", + "reveal": "always" + }, + "type": "shell" } ], "version": "2.0.0" diff --git a/api/wsgi.py b/api/wsgi.py index c1bcc0d22..0b570318a 100644 --- a/api/wsgi.py +++ b/api/wsgi.py @@ -1,10 +1,33 @@ from gevent import monkey +import os # Monkey patch to allow for async actions (aka multiple workers) monkey.patch_all() from qsystem import application, socketio + +def _env_flag(name, default=False): + value = os.environ.get(name) + if value is None: + return default + return value.strip().lower() in {"1", "true", "yes", "on"} + + if __name__ == "__main__": - print("Starting socketio app with debug=" + application.config['REDIS_DEBUG']) - socketio.run(application, debug=True) + debug = _env_flag("WSGI_DEBUG", True) + host = os.environ.get("WSGI_HOST", "0.0.0.0") + port = int(os.environ.get("WSGI_PORT", "5000")) + use_reloader = _env_flag("WSGI_USE_RELOADER", False) + + print( + "Starting socketio app" + + f" with debug={debug}, host={host}, port={port}, reloader={use_reloader}" + ) + socketio.run( + application, + host=host, + port=port, + debug=debug, + use_reloader=use_reloader, + ) diff --git a/documentation/Readme.md b/documentation/Readme.md index a6577e752..f279daa26 100644 --- a/documentation/Readme.md +++ b/documentation/Readme.md @@ -103,7 +103,25 @@ Note: starting Vue.js applications takes a long time for the webpack. When run o
How do I do development on Python code? -The *.vscode/launch.json* file in the repo contains launchers for the API. Select the API you want from the drop-down list and then hit F5 to run it in *gunicorn*. Once the code is running, whenever you save a file *gunicorn* will automatically reload itself with the changes. You can set breakpoints in the code and then test with a browser, newman, or postman. +There are now two recommended local workflows for the API: + +1. **Stable breakpoint debugging in VS Code** + + Use the **queue_management_api** launcher from *.vscode/launch.json* and hit F5. This starts *api/wsgi.py* directly in a single process, which is more reliable for breakpoints on macOS. + + This launcher does **not** auto-reload on file save. Restart the debugger manually after code changes. + +1. **Hot reload in a terminal or VS Code task** + + Run the API with *gunicorn* when you want auto-reload while editing: + + ``` + uv run gunicorn wsgi --bind=0.0.0.0:5000 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 + ``` + + You can also run the **api: gunicorn hot reload** VS Code task. + +The two workflows are separate because the VS Code `debugpy` launcher plus *gunicorn* reloading can fork worker processes in a way that crashes on macOS with Objective-C fork-safety checks enabled. Keeping debugging single-process avoids that restart loop while preserving the existing *gunicorn* hot-reload workflow for normal development.
@@ -288,4 +306,4 @@ From the queue-management/frontend folder run the following command: 1. npm test -You should now see a chromium browser open and go through the tests we created. \ No newline at end of file +You should now see a chromium browser open and go through the tests we created. From cfd57bbd61e6c2fae9f9dd86cabad1e00c1bc519 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 22:16:08 -0700 Subject: [PATCH 17/81] Convert Newman tests to pytest --- .../theq/citizen/citizen_generic_invite.py | 8 +- api/app/tests/api_test_support.py | 90 ++++++ api/app/tests/conftest.py | 250 ++++++++++++++- api/app/tests/test_api_contracts.py | 74 +++++ api/app/tests/test_appointment_flows.py | 168 ++++++++++ api/app/tests/test_booking_exam_flows.py | 189 +++++++++++ api/app/tests/test_flask3_smoke.py | 9 +- api/app/tests/test_queue_flows.py | 301 ++++++++++++++++++ api/setup.cfg | 3 + 9 files changed, 1081 insertions(+), 11 deletions(-) create mode 100644 api/app/tests/api_test_support.py create mode 100644 api/app/tests/test_api_contracts.py create mode 100644 api/app/tests/test_appointment_flows.py create mode 100644 api/app/tests/test_booking_exam_flows.py create mode 100644 api/app/tests/test_queue_flows.py diff --git a/api/app/resources/theq/citizen/citizen_generic_invite.py b/api/app/resources/theq/citizen/citizen_generic_invite.py index 3081458cd..bdf7e8864 100644 --- a/api/app/resources/theq/citizen/citizen_generic_invite.py +++ b/api/app/resources/theq/citizen/citizen_generic_invite.py @@ -22,7 +22,7 @@ from datetime import datetime from app.utilities.auth_util import Role, get_username from app.auth.auth import jwt -from sqlalchemy.orm import contains_eager, raiseload, joinedload +from sqlalchemy.orm import raiseload from sqlalchemy.dialects import postgresql @@ -50,11 +50,10 @@ def find_wait(): @api_call_with_retry def find_citizen(counter_id, active_citizen_state, csr, waiting_period_state): citizen = Citizen.query \ - .options(joinedload(Citizen.service_reqs, innerjoin=True).joinedload(ServiceReq.periods, innerjoin=True).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),raiseload(Citizen.office),raiseload(Citizen.counter),raiseload(Citizen.user)) \ + .options(raiseload(Citizen.office), raiseload(Citizen.counter), raiseload(Citizen.user)) \ .filter_by(counter_id=counter_id, cs_id=active_citizen_state.cs_id, office_id=csr.office_id) \ .join(Citizen.service_reqs) \ .join(ServiceReq.periods) \ - .options(contains_eager(Citizen.service_reqs).contains_eager(ServiceReq.periods)) \ .filter_by(ps_id=waiting_period_state.ps_id) \ .filter(Period.time_end.is_(None)) \ .order_by(Citizen.priority, Citizen.start_time) @@ -65,11 +64,10 @@ def find_citizen(counter_id, active_citizen_state, csr, waiting_period_state): @api_call_with_retry def find_citizen2(active_citizen_state, csr, waiting_period_state): citizen = Citizen.query \ - .options(joinedload(Citizen.service_reqs, innerjoin=True).joinedload(ServiceReq.periods, innerjoin=True).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),raiseload(Citizen.office),raiseload(Citizen.counter),raiseload(Citizen.user)) \ + .options(raiseload(Citizen.office), raiseload(Citizen.counter), raiseload(Citizen.user)) \ .filter_by(cs_id=active_citizen_state.cs_id, office_id=csr.office_id) \ .join(Citizen.service_reqs) \ .join(ServiceReq.periods) \ - .options(contains_eager(Citizen.service_reqs).contains_eager(ServiceReq.periods)) \ .filter_by(ps_id=waiting_period_state.ps_id) \ .filter(Period.time_end.is_(None)) \ .order_by(Citizen.priority, Citizen.citizen_id) diff --git a/api/app/tests/api_test_support.py b/api/app/tests/api_test_support.py new file mode 100644 index 000000000..92b04a768 --- /dev/null +++ b/api/app/tests/api_test_support.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime, time, timedelta, timezone +from typing import Any, Optional +from uuid import uuid4 +from zoneinfo import ZoneInfo + + +@dataclass +class ApiClient: + client: Any + identity_name: str + token: str = "theq-test-token" + + def _normalize_path(self, path: str) -> str: + if path.startswith("/api/"): + return path + if path.startswith("/"): + return f"/api/v1{path}" + return f"/api/v1/{path.lstrip('/')}" + + def _headers(self, headers: Optional[dict[str, str]] = None) -> dict[str, str]: + merged = { + "Authorization": f"Bearer {self.token}", + "X-TheQ-Test-Identity": self.identity_name, + } + if headers: + merged.update(headers) + return merged + + def open(self, path: str, **kwargs): + headers = self._headers(kwargs.pop("headers", None)) + return self.client.open(self._normalize_path(path), headers=headers, **kwargs) + + def get(self, path: str, **kwargs): + return self.open(path, method="GET", **kwargs) + + def post(self, path: str, **kwargs): + return self.open(path, method="POST", **kwargs) + + def put(self, path: str, **kwargs): + return self.open(path, method="PUT", **kwargs) + + def delete(self, path: str, **kwargs): + return self.open(path, method="DELETE", **kwargs) + + +def json_of(response) -> dict[str, Any]: + return response.get_json() + + +def assert_status(response, expected_status: int): + assert response.status_code == expected_status, response.get_data(as_text=True) + + +def flatten_slots(slots_by_day: dict[str, list[dict[str, Any]]]) -> list[tuple[str, dict[str, Any]]]: + flattened: list[tuple[str, dict[str, Any]]] = [] + for day, slots in slots_by_day.items(): + for slot in slots: + flattened.append((day, slot)) + return flattened + + +def first_day_with_slots(slots_by_day: dict[str, list[dict[str, Any]]], minimum_slots: int = 1) -> tuple[str, list[dict[str, Any]]]: + for day, slots in slots_by_day.items(): + if len(slots) >= minimum_slots: + return day, slots + raise AssertionError(f"expected at least one day with {minimum_slots} slot(s), got {slots_by_day}") + + +def slot_window_to_iso(day_key: str, slot: dict[str, Any], timezone_name: str) -> tuple[str, str]: + day_value = datetime.strptime(day_key, "%m/%d/%Y").date() + timezone = ZoneInfo(timezone_name) + start_hour, start_minute = [int(part) for part in slot["start_time"].split(":")] + end_hour, end_minute = [int(part) for part in slot["end_time"].split(":")] + start_dt = datetime.combine(day_value, time(start_hour, start_minute), tzinfo=timezone) + end_dt = datetime.combine(day_value, time(end_hour, end_minute), tzinfo=timezone) + return start_dt.isoformat(), end_dt.isoformat() + + +def future_utc_window(days_from_now: int, start_hour: int = 17, duration_minutes: int = 30) -> tuple[str, str]: + start_dt = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0, hour=start_hour) + start_dt = start_dt + timedelta(days=days_from_now) + end_dt = start_dt + timedelta(minutes=duration_minutes) + return start_dt.isoformat(), end_dt.isoformat() + + +def unique_name(prefix: str) -> str: + return f"{prefix}-{uuid4().hex[:8]}" diff --git a/api/app/tests/conftest.py b/api/app/tests/conftest.py index b0f49bf8b..4ccba9764 100644 --- a/api/app/tests/conftest.py +++ b/api/app/tests/conftest.py @@ -1,3 +1,4 @@ +import copy import importlib import os import sys @@ -6,6 +7,8 @@ import pytest +from app.tests.api_test_support import ApiClient + try: import psycopg2 from psycopg2 import sql @@ -14,6 +17,42 @@ sql = None +TEST_IDENTITIES = { + "internal_ga": { + "username": "cfms-postman-operator", + "identity_provider": "idir", + "realm_access": {"roles": ["internal_user"]}, + "email": "ga@example.com", + "display_name": "GA User", + "family_name": "GA", + }, + "internal_nonqtxn": { + "username": "cfms-postman-non-operator", + "identity_provider": "idir", + "realm_access": {"roles": ["internal_user"]}, + "email": "csr@example.com", + "display_name": "CSR User", + "family_name": "CSR", + }, + "public_user": { + "username": "theq-public-user", + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public@example.com", + "display_name": "Public User", + "family_name": "Public", + }, + "public_user_alt": { + "username": "theq-public-user-alt", + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public-alt@example.com", + "display_name": "Public Alt User", + "family_name": "PublicAlt", + }, +} + + def _db_settings(): return { "engine": os.getenv("TEST_DATABASE_ENGINE", "postgresql+psycopg2"), @@ -36,6 +75,106 @@ def _connect(database_name): ) +def _patch_jwt_manager(): + from flask_jwt_oidc import JwtManager + + if getattr(JwtManager, "_theq_pytest_patched", False): + return + + def passthrough(self, *decorator_args, **decorator_kwargs): + del self, decorator_kwargs + if decorator_args and callable(decorator_args[0]) and len(decorator_args) == 1: + return decorator_args[0] + + def decorator(func): + return func + + return decorator + + def init_app(self, app): + app.extensions["theq_test_jwt"] = self + + JwtManager.has_one_of_roles = passthrough + JwtManager.requires_auth = passthrough + JwtManager.requires_auth_cookie = passthrough + JwtManager.init_app = init_app + JwtManager._theq_pytest_patched = True + + +def _install_identity_loader(app): + if app.config.get("THEQ_TEST_IDENTITY_LOADER"): + return + + from flask import g, request + + app.config["TEST_IDENTITIES"] = TEST_IDENTITIES + app.config["DISABLE_AUTO_REFRESH"] = True + + @app.before_request + def _load_theq_test_identity(): + identity_name = request.headers.get("X-TheQ-Test-Identity") + identity = app.config["TEST_IDENTITIES"].get(identity_name) + if identity: + g.jwt_oidc_token_info = copy.deepcopy(identity) + + app.config["THEQ_TEST_IDENTITY_LOADER"] = True + + +def _stub_integrations(): + import qsystem + from app.utilities.snowplow import SnowPlow + + qsystem.socketio.emit = lambda *args, **kwargs: None + + for method_name in ( + "add_citizen", + "choose_service", + "snowplow_event", + "snowplow_appointment", + ): + setattr(SnowPlow, method_name, staticmethod(lambda *args, **kwargs: None)) + + email_stub = lambda *args, **kwargs: None + email_contents_stub = lambda *args, **kwargs: ("test@example.com", "Subject", "Body") + sms_stub = lambda *args, **kwargs: True + + patch_targets = { + "app.resources.theq.citizen.citizen_add_to_queue": { + "send_email": email_stub, + "get_walkin_spot_confirmation_email_contents": email_contents_stub, + "send_walkin_spot_confirmation_sms": sms_stub, + }, + "app.resources.theq.citizen.citizen_detail": { + "send_email": email_stub, + "get_walkin_reminder_email_contents": email_contents_stub, + "send_walkin_reminder_sms": sms_stub, + }, + "app.resources.theq.user.user": { + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_post": { + "send_email": email_stub, + "get_confirmation_email_contents": email_contents_stub, + "get_blackout_email_contents": email_contents_stub, + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_put": { + "send_email": email_stub, + "get_confirmation_email_contents": email_contents_stub, + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_delete": { + "send_email": email_stub, + "get_cancel_email_contents": email_contents_stub, + }, + } + + for module_name, attributes in patch_targets.items(): + module = importlib.import_module(module_name) + for attribute_name, value in attributes.items(): + setattr(module, attribute_name, value) + + @pytest.fixture(scope="session") def postgres_database(): if psycopg2 is None: @@ -135,7 +274,12 @@ def postgres_database(): @pytest.fixture(scope="session") def app_module(postgres_database): - return importlib.import_module("manage") + del postgres_database + _patch_jwt_manager() + module = importlib.import_module("manage") + _install_identity_loader(module.application) + _stub_integrations() + return module @pytest.fixture(scope="session") @@ -163,3 +307,107 @@ def migrated_database(cli_runner): result = cli_runner.invoke(args=["db", "upgrade"]) assert result.exit_code == 0, result.output return result + + +@pytest.fixture() +def seeded_database(cli_runner, migrated_database, app): + del migrated_database + + with app.app_context(): + from qsystem import db + + db.session.remove() + + result = cli_runner.invoke(args=["bootstrap"]) + assert result.exit_code == 0, result.output + + with app.app_context(): + from qsystem import cache, db + + db.session.remove() + cache.clear() + + return result + + +@pytest.fixture() +def seeded_data(seeded_database, app): + del seeded_database + + with app.app_context(): + from app.models.bookings import ExamType, Invigilator, Room + from app.models.theq import CSR, Channel, Counter, Office, Service + + office_test = Office.query.filter_by(office_name="Test Office").first() + office_limited = Office.query.filter_by(office_name="100 Mile House").first() + quick_trans = Counter.query.filter_by(counter_name="Quick Trans").first() + counter = Counter.query.filter_by(counter_name="Counter").first() + phone_channel = Channel.query.filter_by(channel_name="Phone").first() + email_channel = Channel.query.filter_by(channel_name="Email/Fax/Mail").first() + msp_service = Service.query.filter_by(service_name="Payment - MSP").first() + ptax_service = Service.query.filter_by(service_name="Other - PTAX").first() + limited_office_service = Service.query.filter_by(service_name="Deferment Application").first() + room = Room.query.filter_by(room_name="Boardroom 1").first() + invigilators = Invigilator.query.filter_by(office_id=office_test.office_id).order_by(Invigilator.invigilator_id).all() + exam_type = ExamType.query.order_by(ExamType.exam_type_id).first() + ga = CSR.query.filter_by(username="cfms-postman-operator").first() + non_qtxn = CSR.query.filter_by(username="cfms-postman-non-operator").first() + + return { + "office_ids": { + "test_office": office_test.office_id, + "limited_office": office_limited.office_id, + }, + "office_timezones": { + "test_office": office_test.timezone.timezone_name, + "limited_office": office_limited.timezone.timezone_name, + }, + "counter_ids": { + "quick_trans": quick_trans.counter_id, + "counter": counter.counter_id, + }, + "channel_ids": { + "phone": phone_channel.channel_id, + "email": email_channel.channel_id, + }, + "service_ids": { + "msp": msp_service.service_id, + "ptax": ptax_service.service_id, + "limited_office_service": limited_office_service.service_id, + }, + "csr_ids": { + "ga": ga.csr_id, + "non_qtxn": non_qtxn.csr_id, + }, + "room_id": room.room_id, + "invigilator_ids": [invigilator.invigilator_id for invigilator in invigilators], + "exam_type_id": exam_type.exam_type_id, + } + + +@pytest.fixture() +def api_client_factory(app): + def factory(identity_name): + return ApiClient(app.test_client(), identity_name) + + return factory + + +@pytest.fixture() +def internal_ga_client(api_client_factory): + return api_client_factory("internal_ga") + + +@pytest.fixture() +def internal_nonqtxn_client(api_client_factory): + return api_client_factory("internal_nonqtxn") + + +@pytest.fixture() +def public_client(api_client_factory): + return api_client_factory("public_user") + + +@pytest.fixture() +def public_client_alt(api_client_factory): + return api_client_factory("public_user_alt") diff --git a/api/app/tests/test_api_contracts.py b/api/app/tests/test_api_contracts.py new file mode 100644 index 000000000..756114659 --- /dev/null +++ b/api/app/tests/test_api_contracts.py @@ -0,0 +1,74 @@ +import pytest + +from app.tests.api_test_support import assert_status, json_of + + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def test_health_and_readyz_endpoints(client): + health_response = client.get("/api/v1/healthz/") + ready_response = client.get("/api/v1/readyz/") + + assert_status(health_response, 200) + assert_status(ready_response, 200) + assert json_of(health_response)["message"] == "api is healthy" + assert json_of(ready_response)["message"] == "api is ready" + + +def test_internal_contract_endpoints(internal_ga_client, seeded_data): + del seeded_data + + channels_response = internal_ga_client.get("/channels/") + categories_response = internal_ga_client.get("/categories/") + services_response = internal_ga_client.get("/services/") + offices_response = internal_ga_client.get("/offices/") + rooms_response = internal_ga_client.get("/rooms/") + invigilators_response = internal_ga_client.get("/invigilators/") + exam_types_response = internal_ga_client.get("/exam_types/") + csr_self_response = internal_ga_client.get("/csrs/me/") + + for response in ( + channels_response, + categories_response, + services_response, + offices_response, + rooms_response, + invigilators_response, + exam_types_response, + csr_self_response, + ): + assert_status(response, 200) + + channels = json_of(channels_response)["channels"] + categories = json_of(categories_response)["categories"] + services = json_of(services_response)["services"] + offices = json_of(offices_response)["offices"] + rooms = json_of(rooms_response)["rooms"] + invigilators = json_of(invigilators_response)["invigilators"] + exam_types = json_of(exam_types_response)["exam_types"] + csr_self = json_of(csr_self_response) + + assert any(channel["channel_name"] == "Phone" for channel in channels) + assert any(category["service_name"] == "Property Tax" for category in categories) + assert any(service["service_name"] == "Payment - MSP" for service in services) + assert any(office["office_name"] == "Test Office" for office in offices) + assert any(room["room_name"] == "Boardroom 1" for room in rooms) + assert any(invigilator["invigilator_name"] == "Homer Simpson" for invigilator in invigilators) + assert any(exam_type["exam_type_name"] for exam_type in exam_types) + assert csr_self["csr"]["role"]["role_code"] == "GA" + assert csr_self["csr"]["office"]["office_name"] == "Test Office" + + +def test_public_user_profile_contracts(public_client): + create_response = public_client.post("/users/") + assert_status(create_response, 200) + + created_user = json_of(create_response)[0] + get_me_response = public_client.get("/users/me/") + appointments_response = public_client.get("/users/appointments/") + + assert_status(get_me_response, 200) + assert_status(appointments_response, 200) + assert json_of(get_me_response)[0]["username"] == created_user["username"] + assert "appointments" in json_of(appointments_response) diff --git a/api/app/tests/test_appointment_flows.py b/api/app/tests/test_appointment_flows.py new file mode 100644 index 000000000..1d655c7d9 --- /dev/null +++ b/api/app/tests/test_appointment_flows.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from typing import Optional +from uuid import uuid4 + +import pytest + +from app.tests.api_test_support import ( + assert_status, + first_day_with_slots, + json_of, + slot_window_to_iso, + unique_name, +) + + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _future_window(days_from_now: int, duration_minutes: int = 30) -> tuple[str, str]: + start = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + timedelta(days=days_from_now) + end = start + timedelta(minutes=duration_minutes) + return start.isoformat(), end.isoformat() + + +def _create_internal_appointment(api_client, seeded_data, *, days_from_now: int, recurring_uuid: Optional[str] = None): + start_time, end_time = _future_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal appointment", + "citizen_name": unique_name("internal-appt"), + "contact_information": "internal@example.com", + "recurring_uuid": recurring_uuid, + }, + ) + assert_status(response, 201) + return json_of(response)["appointment"] + + +def _public_slot_payload(public_client, seeded_data, *, minimum_slots: int = 1): + slots_response = public_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + assert_status(slots_response, 200) + day_key, slots = first_day_with_slots(json_of(slots_response), minimum_slots=minimum_slots) + start_time, end_time = slot_window_to_iso(day_key, slots[0], seeded_data["office_timezones"]["limited_office"]) + return { + "service_id": seeded_data["service_ids"]["limited_office_service"], + "office_id": seeded_data["office_ids"]["limited_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Public appointment", + "citizen_name": "Codex Public User", + "contact_information": "public@example.com", + }, day_key, slots + + +def test_internal_appointment_crud_and_recurring_workflows(internal_ga_client, seeded_data): + appointment = _create_internal_appointment(internal_ga_client, seeded_data, days_from_now=2) + + list_response = internal_ga_client.get("/appointments/") + detail_response = internal_ga_client.get(f"/appointments/{appointment['appointment_id']}/") + update_response = internal_ga_client.put( + f"/appointments/{appointment['appointment_id']}/", + json={"comments": "Internal appointment updated"}, + ) + delete_response = internal_ga_client.delete(f"/appointments/{appointment['appointment_id']}/") + + assert_status(list_response, 200) + assert_status(detail_response, 200) + assert_status(update_response, 200) + assert_status(delete_response, 204) + assert any(item["appointment_id"] == appointment["appointment_id"] for item in json_of(list_response)["appointments"]) + assert json_of(update_response)["appointment"]["comments"] == "Internal appointment updated" + + recurring_uuid = str(uuid4()) + first_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=3, + recurring_uuid=recurring_uuid, + ) + second_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=4, + recurring_uuid=recurring_uuid, + ) + + recurring_update = internal_ga_client.put( + f"/appointments/recurring/{recurring_uuid}", + json={"comments": "Recurring appointment updated"}, + ) + recurring_delete = internal_ga_client.delete(f"/appointments/recurring/{recurring_uuid}") + final_list = internal_ga_client.get("/appointments/") + + assert_status(recurring_update, 200) + assert_status(recurring_delete, 204) + assert_status(final_list, 200) + remaining_ids = {item["appointment_id"] for item in json_of(final_list)["appointments"]} + assert first_recurring["appointment_id"] not in remaining_ids + assert second_recurring["appointment_id"] not in remaining_ids + + +def test_public_user_profile_and_appointment_workflows(public_client, seeded_data): + create_user_response = public_client.post("/users/") + assert_status(create_user_response, 200) + created_user = json_of(create_user_response)[0] + + update_user_response = public_client.put( + f"/users/{created_user['user_id']}/", + json={ + "email": "updated-public@example.com", + "telephone": "2505550100", + "send_email_reminders": True, + "send_sms_reminders": True, + }, + ) + get_me_response = public_client.get("/users/me/") + + assert_status(update_user_response, 200) + assert_status(get_me_response, 200) + assert json_of(update_user_response)[0]["email"] == "updated-public@example.com" + + appointment_payload, day_key, slots = _public_slot_payload(public_client, seeded_data, minimum_slots=2) + create_appointment_response = public_client.post("/appointments/", json=appointment_payload) + assert_status(create_appointment_response, 201) + appointment = json_of(create_appointment_response)["appointment"] + + list_response = public_client.get("/users/appointments/") + assert_status(list_response, 200) + assert any(item["appointment_id"] == appointment["appointment_id"] for item in json_of(list_response)["appointments"]) + + second_start_time, second_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + max_limit_response = public_client.post( + "/appointments/", + json={ + **appointment_payload, + "start_time": second_start_time, + "end_time": second_end_time, + }, + ) + assert_status(max_limit_response, 400) + assert json_of(max_limit_response)["code"] == "MAX_NO_OF_APPOINTMENTS_REACHED" + + delete_response = public_client.delete(f"/appointments/{appointment['appointment_id']}/") + assert_status(delete_response, 204) + + +def test_appointment_slots_endpoint(public_client, seeded_data): + response = public_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + + assert_status(response, 200) + day_key, slots = first_day_with_slots(json_of(response)) + assert day_key + assert slots[0]["no_of_slots"] >= 1 diff --git a/api/app/tests/test_booking_exam_flows.py b/api/app/tests/test_booking_exam_flows.py new file mode 100644 index 000000000..ea0209f39 --- /dev/null +++ b/api/app/tests/test_booking_exam_flows.py @@ -0,0 +1,189 @@ +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from typing import Optional +from uuid import uuid4 +from zoneinfo import ZoneInfo + +import pytest + +from app.tests.api_test_support import assert_status, json_of, unique_name + + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _future_window(days_from_now: int, duration_hours: int = 2) -> tuple[str, str]: + start = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + timedelta(days=days_from_now) + end = start + timedelta(hours=duration_hours) + return start.isoformat(), end.isoformat() + + +def _create_booking(api_client, seeded_data, *, days_from_now: int, recurring_uuid: Optional[str] = None): + start_time, end_time = _future_window(days_from_now) + response = api_client.post( + "/bookings/", + json={ + "booking_name": unique_name("booking"), + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": seeded_data["office_ids"]["test_office"], + "room_id": seeded_data["room_id"], + "start_time": start_time, + "end_time": end_time, + "recurring_uuid": recurring_uuid, + "invigilator_id": [seeded_data["invigilator_ids"][0]], + }, + ) + assert_status(response, 201) + return json_of(response)["booking"] + + +def _create_exam(api_client, seeded_data, booking_id: int, *, event_id: str): + expiry_date = (datetime.now(timezone.utc) + timedelta(days=30)).replace(microsecond=0).isoformat() + response = api_client.post( + "/exams/", + json={ + "booking_id": booking_id, + "event_id": event_id, + "exam_method": "paper", + "exam_name": unique_name("exam"), + "exam_type_id": seeded_data["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Codex exam notes", + "number_of_students": 19, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": expiry_date, + }, + ) + assert_status(response, 201) + return json_of(response)["exam"] + + +def test_booking_crud_and_recurring_workflows(internal_ga_client, seeded_data): + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + list_response = internal_ga_client.get("/bookings/") + detail_response = internal_ga_client.get(f"/bookings/{booking['booking_id']}/") + update_response = internal_ga_client.put( + f"/bookings/{booking['booking_id']}/", + json={ + "booking_name": "Updated single booking", + "booking_contact_information": "updated-booking@example.com", + "invigilator_id": [seeded_data["invigilator_ids"][1]], + }, + ) + delete_response = internal_ga_client.delete(f"/bookings/{booking['booking_id']}/") + + assert_status(list_response, 200) + assert_status(detail_response, 200) + assert_status(update_response, 200) + assert_status(delete_response, 204) + assert any(item["booking_id"] == booking["booking_id"] for item in json_of(list_response)["bookings"]) + assert json_of(update_response)["booking"]["booking_name"] == "Updated single booking" + + recurring_uuid = str(uuid4()) + first_recurring = _create_booking(internal_ga_client, seeded_data, days_from_now=3, recurring_uuid=recurring_uuid) + second_recurring = _create_booking(internal_ga_client, seeded_data, days_from_now=4, recurring_uuid=recurring_uuid) + + recurring_update = internal_ga_client.put( + f"/bookings/recurring/{recurring_uuid}", + json={"booking_name": "Recurring booking updated"}, + ) + recurring_delete = internal_ga_client.delete(f"/bookings/recurring/{recurring_uuid}") + final_list = internal_ga_client.get("/bookings/") + + assert_status(recurring_update, 200) + assert_status(recurring_delete, 204) + assert_status(final_list, 200) + final_booking_ids = {item["booking_id"] for item in json_of(final_list)["bookings"]} + assert first_recurring["booking_id"] not in final_booking_ids + assert second_recurring["booking_id"] not in final_booking_ids + + +def test_exam_crud_event_lookup_and_export(internal_ga_client, seeded_data): + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + event_id = str(9000 + booking["booking_id"]) + exam = _create_exam(internal_ga_client, seeded_data, booking["booking_id"], event_id=event_id) + + detail_response = internal_ga_client.get(f"/exams/{exam['exam_id']}/") + list_response = internal_ga_client.get("/exams/") + update_response = internal_ga_client.put( + f"/exams/{exam['exam_id']}/", + json={"exam_name": "Updated exam name", "notes": "Updated notes"}, + ) + event_lookup_response = internal_ga_client.get(f"/exams/event_id/{event_id}/") + + booking_start = datetime.fromisoformat(booking["start_time"].replace("Z", "+00:00")) + export_date = booking_start.astimezone( + ZoneInfo(seeded_data["office_timezones"]["test_office"]) + ).date().isoformat() + export_response = internal_ga_client.get(f"/exams/export/?start_date={export_date}&end_date={export_date}") + delete_response = internal_ga_client.delete(f"/exams/{exam['exam_id']}/") + post_delete_list = internal_ga_client.get("/exams/") + + assert_status(detail_response, 200) + assert_status(list_response, 200) + assert_status(update_response, 201) + assert_status(event_lookup_response, 200) + assert_status(export_response, 200) + assert_status(delete_response, 204) + assert_status(post_delete_list, 200) + + assert json_of(detail_response)["exam"]["event_id"] == event_id + assert any(item["exam_id"] == exam["exam_id"] for item in json_of(list_response)["exams"]) + assert json_of(update_response)["exam"]["exam_name"] == "Updated exam name" + assert json_of(event_lookup_response)["message"] is True + export_body = export_response.get_data(as_text=True) + assert "Office Name,Exam Type,Exam ID,Exam Name" in export_body + assert "Updated exam name" in export_body + assert all(item["exam_id"] != exam["exam_id"] for item in json_of(post_delete_list)["exams"]) + + +def test_invigilator_lists_and_shadow_count_mutations(internal_ga_client): + list_response = internal_ga_client.get("/invigilators/") + offsite_response = internal_ga_client.get("/invigilators/offsite/") + + assert_status(list_response, 200) + assert_status(offsite_response, 200) + + invigilators = json_of(list_response)["invigilators"] + offsite_invigilators = json_of(offsite_response)["invigilators"] + + assert invigilators + assert offsite_invigilators + + first_invigilator = invigilators[0] + assert first_invigilator["shadow_count"] == 2 + assert first_invigilator["shadow_flag"] == "Y" + assert any( + invigilator["invigilator_name"] == "Pest 1" for invigilator in offsite_invigilators + ) + + subtract_response = internal_ga_client.put( + f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=True&add=False" + ) + assert_status(subtract_response, 200) + subtracted = json_of(subtract_response)["invigilator"] + + assert subtracted["invigilator_id"] == first_invigilator["invigilator_id"] + assert subtracted["contact_email"] == first_invigilator["contact_email"] + assert subtracted["contact_phone"] == first_invigilator["contact_phone"] + assert subtracted["invigilator_notes"] == first_invigilator["invigilator_notes"] + assert subtracted["shadow_count"] == 1 + assert subtracted["shadow_flag"] == "N" + + add_response = internal_ga_client.put( + f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=False&add=True" + ) + assert_status(add_response, 200) + added_back = json_of(add_response)["invigilator"] + + assert added_back["invigilator_id"] == first_invigilator["invigilator_id"] + assert added_back["contact_email"] == first_invigilator["contact_email"] + assert added_back["contact_phone"] == first_invigilator["contact_phone"] + assert added_back["invigilator_notes"] == first_invigilator["invigilator_notes"] + assert added_back["shadow_count"] == 2 + assert added_back["shadow_flag"] == "Y" diff --git a/api/app/tests/test_flask3_smoke.py b/api/app/tests/test_flask3_smoke.py index ef255a213..3ed9f8bf0 100644 --- a/api/app/tests/test_flask3_smoke.py +++ b/api/app/tests/test_flask3_smoke.py @@ -6,12 +6,11 @@ def _unwrap(func): return inspect.unwrap(func) -def test_routes_command_lists_admin_and_healthz(cli_runner): - result = cli_runner.invoke(args=["routes"]) +def test_application_url_map_contains_admin_and_healthz(app): + routes = {rule.rule for rule in app.url_map.iter_rules()} - assert result.exit_code == 0, result.output - assert "/admin/" in result.output - assert "/api/v1/healthz/" in result.output + assert "/admin/" in routes + assert "/api/v1/healthz/" in routes def test_admin_index_renders_without_bootstrap3_assets(client): diff --git a/api/app/tests/test_queue_flows.py b/api/app/tests/test_queue_flows.py new file mode 100644 index 000000000..31ad7bfed --- /dev/null +++ b/api/app/tests/test_queue_flows.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +from typing import Optional + +import pytest + +from app.tests.api_test_support import assert_status, json_of + + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _create_citizen(api_client, position: int, *, name: str, comments: Optional[str] = None): + payload = {"citizen_name": name} + if comments is not None: + payload["citizen_comments"] = comments + + response = api_client.post( + f"/citizens/{position}/add_citizen/", + json=payload, + ) + assert_status(response, 201) + return json_of(response)["citizen"] + + +def _update_citizen(api_client, citizen_id: int, **payload): + response = api_client.put(f"/citizens/{citizen_id}/", json=payload) + assert_status(response, 200) + return json_of(response)["citizen"] + + +def _create_service_request(api_client, citizen_id: int, *, service_id: int, channel_id: int, quantity: int): + response = api_client.post( + "/service_requests/", + json={ + "service_request": { + "citizen_id": citizen_id, + "service_id": service_id, + "channel_id": channel_id, + "quantity": quantity, + } + }, + ) + assert_status(response, 201) + return json_of(response)["service_request"] + + +def _citizen_detail(api_client, citizen_id: int): + response = api_client.get(f"/citizens/{citizen_id}/") + assert_status(response, 200) + return json_of(response)["citizen"] + + +def _queue_ids(api_client) -> list[int]: + response = api_client.get("/citizens/") + assert_status(response, 200) + return [citizen["citizen_id"] for citizen in json_of(response)["citizens"]] + + +def test_qt1_specific_invite_additional_service_and_reactivate(internal_ga_client, seeded_data, app): + citizen = _create_citizen(internal_ga_client, 0, name="QT1 Citizen", comments="Needs property tax") + updated = _update_citizen( + internal_ga_client, + citizen["citizen_id"], + citizen_name="QT1 Citizen", + citizen_comments="Needs property tax", + qt_xn_citizen_ind=0, + counter_id=seeded_data["counter_ids"]["counter"], + ) + + first_service = _create_service_request( + internal_ga_client, + updated["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=3, + ) + assert first_service["quantity"] == 3 + + add_to_queue_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/add_to_queue/") + specific_invite_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/invite/") + begin_service_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/begin_service/") + + assert_status(add_to_queue_response, 200) + assert_status(specific_invite_response, 200) + assert_status(begin_service_response, 200) + + second_service = _create_service_request( + internal_ga_client, + updated["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + reactivate_response = internal_ga_client.post(f"/service_requests/{first_service['sr_id']}/activate/") + finish_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/finish_service/") + + assert second_service["service_id"] == seeded_data["service_ids"]["msp"] + assert_status(reactivate_response, 200) + assert_status(finish_response, 200) + assert _queue_ids(internal_ga_client) == [] + + with app.app_context(): + from app.models.theq import Citizen, ServiceReq + + citizen_model = Citizen.query.filter_by(citizen_id=updated["citizen_id"]).first() + sr_models = ServiceReq.query.filter_by(citizen_id=updated["citizen_id"]).order_by(ServiceReq.sr_number).all() + assert citizen_model.cs.cs_state_name == "Received Services" + assert [service_request.sr_state.sr_code for service_request in sr_models] == ["Complete", "Complete"] + + +def test_qt2_begin_hold_resume_finish_flow(internal_ga_client, seeded_data): + citizen = _create_citizen(internal_ga_client, 0, name="QT2 Citizen") + _update_citizen( + internal_ga_client, + citizen["citizen_id"], + citizen_name="QT2 Citizen", + counter_id=seeded_data["counter_ids"]["counter"], + ) + _create_service_request( + internal_ga_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=2, + ) + + assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/"), 200) + hold_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/place_on_hold/") + service_requests_response = internal_ga_client.get(f"/citizens/{citizen['citizen_id']}/service_requests/") + resume_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + finish_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") + + assert_status(hold_response, 200) + assert_status(service_requests_response, 200) + assert_status(resume_response, 200) + assert_status(finish_response, 200) + assert _queue_ids(internal_ga_client) == [] + + +def test_qt3_citizen_leaves_after_create(internal_ga_client, app): + citizen = _create_citizen(internal_ga_client, 0, name="QT3 Citizen") + leave_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/citizen_left/") + + assert_status(leave_response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + citizen_model = Citizen.query.filter_by(citizen_id=citizen["citizen_id"]).first() + assert citizen_model.cs.cs_state_name == "Left before receiving services" + + +def test_qt4_citizen_leaves_after_waiting(internal_ga_client, seeded_data, app): + citizen = _create_citizen(internal_ga_client, 0, name="QT4 Citizen") + _create_service_request( + internal_ga_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=1, + ) + assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/add_to_queue/"), 200) + leave_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/citizen_left/") + + assert_status(leave_response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + citizen_model = Citizen.query.filter_by(citizen_id=citizen["citizen_id"]).first() + assert citizen_model.cs.cs_state_name == "Left before receiving services" + + +def test_qt5_update_service_request_quantity_and_service(internal_ga_client, seeded_data, app): + citizen = _create_citizen(internal_ga_client, 0, name="QT5 Citizen") + service_request = _create_service_request( + internal_ga_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=3, + ) + assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/"), 200) + + quantity_update = internal_ga_client.put( + f"/service_requests/{service_request['sr_id']}/", + json={"quantity": 5}, + ) + service_update = internal_ga_client.put( + f"/service_requests/{service_request['sr_id']}/", + json={"service_id": seeded_data["service_ids"]["msp"]}, + ) + finish_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") + + assert_status(quantity_update, 200) + assert_status(service_update, 200) + assert_status(finish_response, 200) + + with app.app_context(): + from app.models.theq import ServiceReq + + service_request_model = ServiceReq.query.filter_by(sr_id=service_request["sr_id"]).first() + assert service_request_model.quantity == 5 + assert service_request_model.service_id == seeded_data["service_ids"]["msp"] + + +def test_qt6_generic_invite_prefers_quick_trans_counter(internal_ga_client, seeded_data): + first_citizen = _create_citizen(internal_ga_client, 0, name="QT6 First") + _update_citizen( + internal_ga_client, + first_citizen["citizen_id"], + citizen_name="QT6 First", + qt_xn_citizen_ind=0, + counter_id=seeded_data["counter_ids"]["counter"], + ) + _create_service_request( + internal_ga_client, + first_citizen["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=1, + ) + assert_status(internal_ga_client.post(f"/citizens/{first_citizen['citizen_id']}/add_to_queue/"), 200) + + second_citizen = _create_citizen(internal_ga_client, 1, name="QT6 Second") + _update_citizen( + internal_ga_client, + second_citizen["citizen_id"], + citizen_name="QT6 Second", + qt_xn_citizen_ind=1, + counter_id=seeded_data["counter_ids"]["quick_trans"], + ) + _create_service_request( + internal_ga_client, + second_citizen["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/add_to_queue/"), 200) + + first_invite = internal_ga_client.post("/citizens/invite/", json={}) + assert_status(first_invite, 200) + assert json_of(first_invite)["citizen"]["citizen_id"] == second_citizen["citizen_id"] + assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/begin_service/"), 200) + assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/finish_service/"), 200) + + second_invite = internal_ga_client.post("/citizens/invite/", json={}) + assert_status(second_invite, 200) + assert json_of(second_invite)["citizen"]["citizen_id"] == first_citizen["citizen_id"] + assert_status(internal_ga_client.post(f"/citizens/{first_citizen['citizen_id']}/citizen_left/"), 200) + assert _queue_ids(internal_ga_client) == [] + + +def test_qt7_generic_invite_prefers_standard_counter(internal_nonqtxn_client, seeded_data): + first_citizen = _create_citizen(internal_nonqtxn_client, 0, name="QT7 First") + _update_citizen( + internal_nonqtxn_client, + first_citizen["citizen_id"], + citizen_name="QT7 First", + qt_xn_citizen_ind=1, + counter_id=seeded_data["counter_ids"]["quick_trans"], + ) + _create_service_request( + internal_nonqtxn_client, + first_citizen["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + assert_status(internal_nonqtxn_client.post(f"/citizens/{first_citizen['citizen_id']}/add_to_queue/"), 200) + + second_citizen = _create_citizen(internal_nonqtxn_client, 1, name="QT7 Second") + _update_citizen( + internal_nonqtxn_client, + second_citizen["citizen_id"], + citizen_name="QT7 Second", + qt_xn_citizen_ind=0, + counter_id=seeded_data["counter_ids"]["counter"], + ) + _create_service_request( + internal_nonqtxn_client, + second_citizen["citizen_id"], + service_id=seeded_data["service_ids"]["ptax"], + channel_id=seeded_data["channel_ids"]["phone"], + quantity=1, + ) + assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/add_to_queue/"), 200) + + first_invite = internal_nonqtxn_client.post("/citizens/invite/", json={}) + assert_status(first_invite, 200) + assert json_of(first_invite)["citizen"]["citizen_id"] == second_citizen["citizen_id"] + assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/begin_service/"), 200) + assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/finish_service/"), 200) + + second_invite = internal_nonqtxn_client.post("/citizens/invite/", json={}) + assert_status(second_invite, 200) + assert json_of(second_invite)["citizen"]["citizen_id"] == first_citizen["citizen_id"] + assert_status(internal_nonqtxn_client.post(f"/citizens/{first_citizen['citizen_id']}/citizen_left/"), 200) + assert _queue_ids(internal_nonqtxn_client) == [] diff --git a/api/setup.cfg b/api/setup.cfg index d9d7bdc8e..2e38aa834 100755 --- a/api/setup.cfg +++ b/api/setup.cfg @@ -47,5 +47,8 @@ lines_after_imports = 2 addopts = --cov=app --cov-report html:htmlcov --cov-report xml:coverage.xml testpaths = app/tests python_files = test*.py +markers = + contracts: Stable API contract and smoke assertions for seeded local test data. + flows: Stateful end-to-end API regression workflows translated from the legacy Postman collections. filterwarnings = ignore::UserWarning From d0c47acf1fcd151c028e4d7394ae21a548ada99d Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 25 Mar 2026 23:00:13 -0700 Subject: [PATCH 18/81] Upgrade marshmallow to 4.x --- api/app/schemas/__init__.py | 17 ++ api/app/schemas/bookings/booking_schema.py | 2 +- api/app/schemas/bookings/exam_schema.py | 3 +- api/app/schemas/theq/csr_schema.py | 2 +- api/app/schemas/theq/service_schema.py | 15 +- api/app/tests/test_schema_compatibility.py | 209 +++++++++++++++++++++ api/pyproject.toml | 2 +- api/requirements.txt | 9 +- api/requirements_dev.txt | 9 +- api/uv.lock | 11 +- 10 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 api/app/tests/test_schema_compatibility.py diff --git a/api/app/schemas/__init__.py b/api/app/schemas/__init__.py index fc5397d63..bf29488ab 100644 --- a/api/app/schemas/__init__.py +++ b/api/app/schemas/__init__.py @@ -11,6 +11,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.''' +from collections.abc import Mapping + from marshmallow import EXCLUDE from qsystem import ma @@ -21,3 +23,18 @@ class BaseSchema(ma.SQLAlchemySchema): class Meta: load_instance = True unknown = EXCLUDE + + def validate(self, data, *, many=None, partial=None): + """Support legacy validation calls that pass ORM instances.""" + if many is None: + many = self.many + + if isinstance(data, Mapping): + return super().validate(data, many=many, partial=partial) + + if isinstance(data, (list, tuple)): + if all(isinstance(item, Mapping) for item in data): + return super().validate(data, many=many, partial=partial) + return {} + + return {} if data is not None else super().validate(data, many=many, partial=partial) diff --git a/api/app/schemas/bookings/booking_schema.py b/api/app/schemas/bookings/booking_schema.py index c83f70645..f1e8c13f1 100644 --- a/api/app/schemas/bookings/booking_schema.py +++ b/api/app/schemas/bookings/booking_schema.py @@ -69,7 +69,7 @@ def update_invigilators(self, data): data['invigilators'] = invigilator_list return data - @post_dump(pass_many=True) + @post_dump(pass_collection=True) def fix_invigilators(self, data, many, **kwargs): if not many: data = self.update_invigilators(data) diff --git a/api/app/schemas/bookings/exam_schema.py b/api/app/schemas/bookings/exam_schema.py index 391e22956..7490b09ed 100644 --- a/api/app/schemas/bookings/exam_schema.py +++ b/api/app/schemas/bookings/exam_schema.py @@ -46,7 +46,7 @@ class Meta(BaseSchema.Meta): exam_method = fields.Str() exam_name = fields.Str() exam_received = fields.Int() - exam_received_date = fields.DateTime(allow_none=True, datetimeformat='%Y-%m-%dT%H:%M:%SZ') + exam_received_date = fields.DateTime(allow_none=True, format='%Y-%m-%dT%H:%M:%SZ') exam_type_id = fields.Int() examinee_name = fields.Str(allow_none=True) examinee_phone = fields.Str(allow_none=True) @@ -81,4 +81,3 @@ class Meta(BaseSchema.Meta): invigilator = fields.Nested(InvigilatorSchema()) office = fields.Nested(OfficeSchema(only=('appointments_enabled_ind', 'exams_enabled_ind', 'office_id', 'office_name', 'office_number', 'timezone'))) - diff --git a/api/app/schemas/theq/csr_schema.py b/api/app/schemas/theq/csr_schema.py index 36719e9e1..bb0b3c235 100644 --- a/api/app/schemas/theq/csr_schema.py +++ b/api/app/schemas/theq/csr_schema.py @@ -42,7 +42,7 @@ class Meta(BaseSchema.Meta): finance_designate = fields.Int() ita2_designate = fields.Int() - @post_dump(pass_many=True) + @post_dump(pass_collection=True) def add_counter_id(self, data, many, **kwargs): if not many: data['counter'] = data['counter_id'] diff --git a/api/app/schemas/theq/service_schema.py b/api/app/schemas/theq/service_schema.py index d29c5b8ea..50001a595 100644 --- a/api/app/schemas/theq/service_schema.py +++ b/api/app/schemas/theq/service_schema.py @@ -14,6 +14,7 @@ from marshmallow import fields from app.models.theq import Service +from app.utilities.yesno import YesNo from qsystem import ma from app.schemas import BaseSchema @@ -29,7 +30,7 @@ class Meta(BaseSchema.Meta): service_code = fields.Str(dump_only=True) service_name = fields.Str(dump_only=True) service_desc = fields.Str(dump_only=True) - parent = fields.Nested('self', only=('service_name',)) + parent = fields.Nested(lambda: ServiceSchema(only=('service_name',))) parent_id = fields.Int(dump_only=True) deleted = fields.DateTime(dump_only=True) prefix = fields.Str(dump_only=True) @@ -41,4 +42,14 @@ class Meta(BaseSchema.Meta): timeslot_duration = fields.Int(dump_only=True) email_paragraph = fields.Str(dump_only=True) css_colour = fields.Str(dump_only=True) - is_dlkt = fields.Boolean() + is_dlkt = fields.Method(serialize="get_is_dlkt", deserialize="load_is_dlkt", allow_none=True) + + def get_is_dlkt(self, obj): + if obj.is_dlkt is None: + return None + return obj.is_dlkt == YesNo.YES + + def load_is_dlkt(self, value): + if value is None: + return None + return YesNo.YES if value else YesNo.NO diff --git a/api/app/tests/test_schema_compatibility.py b/api/app/tests/test_schema_compatibility.py new file mode 100644 index 000000000..acb4c90b4 --- /dev/null +++ b/api/app/tests/test_schema_compatibility.py @@ -0,0 +1,209 @@ +import importlib +import inspect +import pkgutil +from datetime import datetime, timezone + + +def _schema_classes(): + import app.schemas as schema_package + from app.schemas import BaseSchema + + schema_classes = [] + for _, module_name, _ in pkgutil.walk_packages( + schema_package.__path__, prefix=f"{schema_package.__name__}." + ): + module = importlib.import_module(module_name) + for value in module.__dict__.values(): + if ( + inspect.isclass(value) + and issubclass(value, BaseSchema) + and value is not BaseSchema + and value.__module__ == module_name + ): + schema_classes.append(value) + + return sorted(schema_classes, key=lambda schema_cls: schema_cls.__name__) + + +def test_all_schema_classes_instantiate(app, seeded_database): + del seeded_database + + with app.app_context(): + schema_classes = _schema_classes() + assert schema_classes + + for schema_cls in schema_classes: + schema = schema_cls() + assert schema.fields + + +def test_schema_validate_is_safe_for_orm_objects(app, seeded_data): + with app.app_context(): + from app.models.theq import Citizen, Service + from app.schemas.theq import CitizenSchema, ServiceSchema + from app.utilities.yesno import YesNo + + service = Service( + service_id=1002, + service_code="VALIDATE", + service_name="Validate Service", + service_desc="Validate service", + prefix="VL", + display_dashboard_ind=1, + actual_service_ind=1, + is_dlkt=YesNo.NO, + ) + citizens = Citizen.query.limit(2).all() + + assert ServiceSchema().validate(service) == {} + assert CitizenSchema(many=True).validate(citizens) == {} + + +def test_service_schema_serializes_parent_name(app, seeded_data): + del seeded_data + + with app.app_context(): + from app.models.theq import Service + from app.schemas.theq import ServiceSchema + from app.utilities.yesno import YesNo + + parent = Service( + service_id=1000, + service_code="PARENT", + service_name="Parent Service", + service_desc="Parent service", + prefix="PR", + display_dashboard_ind=1, + actual_service_ind=1, + ) + service = Service( + service_id=1001, + service_code="CHILD", + service_name="Child Service", + service_desc="Child service", + prefix="CH", + display_dashboard_ind=1, + actual_service_ind=1, + is_dlkt=YesNo.YES, + parent=parent, + ) + + dumped = ServiceSchema().dump(service) + + assert dumped["parent"] == {"service_name": "Parent Service"} + assert dumped["is_dlkt"] is True + + +def test_exam_schema_preserves_exam_received_date_format(app, seeded_data): + with app.app_context(): + from app.models.bookings import Exam, ExamType, Invigilator + from app.models.theq import Office + from app.schemas.bookings import ExamSchema + from qsystem import db + + office = db.session.get(Office, seeded_data["office_ids"]["test_office"]) + exam_type = db.session.get(ExamType, seeded_data["exam_type_id"]) + invigilator = db.session.get(Invigilator, seeded_data["invigilator_ids"][0]) + + exam = Exam( + exam_id=2001, + office_id=office.office_id, + office=office, + exam_type_id=exam_type.exam_type_id, + exam_type=exam_type, + invigilator_id=invigilator.invigilator_id, + invigilator=invigilator, + exam_name="Knowledge Test", + exam_method="paper", + exam_received_date=datetime(2026, 3, 25, 12, 30, tzinfo=timezone.utc), + exam_written_ind=0, + ) + exam.exam_received = 1 + exam.exam_returned_ind = 0 + exam.receipt_number = "R-001" + exam.fees = "10.00" + + dumped = ExamSchema().dump(exam) + + assert dumped["exam_received_date"] == "2026-03-25T12:30:00Z" + + +def test_booking_schema_post_dump_supports_single_and_many(app, seeded_data): + with app.app_context(): + from app.models.bookings import Booking, Invigilator, Room + from app.models.theq import Office + from app.schemas.bookings import BookingSchema + from qsystem import db + + office = db.session.get(Office, seeded_data["office_ids"]["test_office"]) + room = db.session.get(Room, seeded_data["room_id"]) + invigilators = [ + db.session.get(Invigilator, invigilator_id) + for invigilator_id in seeded_data["invigilator_ids"][:2] + ] + + booking = Booking( + booking_id=3001, + office_id=office.office_id, + office=office, + room_id=room.room_id, + room=room, + start_time=datetime(2026, 3, 25, 16, 0, tzinfo=timezone.utc), + end_time=datetime(2026, 3, 25, 17, 0, tzinfo=timezone.utc), + booking_name="Board Meeting", + sbc_staff_invigilated=0, + stat_flag=False, + invigilators=invigilators, + ) + + schema = BookingSchema() + dumped_single = schema.dump(booking) + dumped_many = schema.dump([booking], many=True) + + expected_invigilators = [invigilator.invigilator_id for invigilator in invigilators] + assert dumped_single["invigilators"] == expected_invigilators + assert dumped_many[0]["invigilators"] == expected_invigilators + + +def test_csr_schema_post_dump_supports_single_and_many(app, seeded_data): + del seeded_data + + with app.app_context(): + from app.models.theq import CSR + from app.schemas.theq import CSRSchema + + csrs = CSR.query.order_by(CSR.csr_id).limit(2).all() + assert len(csrs) == 2 + + schema = CSRSchema() + dumped_single = schema.dump(csrs[0]) + dumped_many = schema.dump(csrs, many=True) + + assert dumped_single["counter"] == dumped_single["counter_id"] + assert [csr["counter"] for csr in dumped_many] == [csr["counter_id"] for csr in dumped_many] + + +def test_appointment_availability_schema_smoke(app, seeded_data): + with app.app_context(): + from app.models.bookings import Appointment + from app.schemas.bookings.appointment_availability_schema import ( + AppointmentAvailabilitySchema, + ) + + appointment = Appointment( + appointment_id=4001, + office_id=seeded_data["office_ids"]["test_office"], + service_id=seeded_data["service_ids"]["msp"], + citizen_id=1234, + start_time=datetime(2026, 3, 25, 18, 0, tzinfo=timezone.utc), + end_time=datetime(2026, 3, 25, 18, 30, tzinfo=timezone.utc), + citizen_name="Pat Citizen", + blackout_flag="N", + ) + + dumped = AppointmentAvailabilitySchema().dump(appointment) + + assert dumped["appointment_id"] == 4001 + assert dumped["office_id"] == seeded_data["office_ids"]["test_office"] + assert dumped["service_id"] == seeded_data["service_ids"]["msp"] + assert dumped["citizen_name"] == "Pat Citizen" diff --git a/api/pyproject.toml b/api/pyproject.toml index 7343af330..f63f84b65 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -53,7 +53,7 @@ dependencies = [ "lazy-object-proxy==1.10.0", "Mako==1.3.3", "MarkupSafe==2.1.5", - "marshmallow==3.21.2", + "marshmallow==4.2.2", "marshmallow-sqlalchemy==1.4.2", "minio==7.2.20", "oauthlib==3.2.2", diff --git a/api/requirements.txt b/api/requirements.txt index ae6fb16d8..ec0cd0e43 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --cache-dir /tmp/uv-cache --format requirements-txt --no-emit-project --output-file requirements.txt +# uv export --frozen --format requirements-txt --no-emit-project --output-file requirements.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc @@ -437,9 +437,9 @@ markupsafe==2.1.5 \ # queue-api # werkzeug # wtforms -marshmallow==3.21.2 \ - --hash=sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1 \ - --hash=sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56 +marshmallow==4.2.2 \ + --hash=sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121 \ + --hash=sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f # via # flask-marshmallow # marshmallow-sqlalchemy @@ -468,7 +468,6 @@ packaging==24.0 \ # via # gunicorn # kombu - # marshmallow # pytest # queue-api platformdirs==4.9.4 \ diff --git a/api/requirements_dev.txt b/api/requirements_dev.txt index 96e2a4eac..5bb52208c 100644 --- a/api/requirements_dev.txt +++ b/api/requirements_dev.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --cache-dir /tmp/uv-cache --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt +# uv export --frozen --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc @@ -437,9 +437,9 @@ markupsafe==2.1.5 \ # queue-api # werkzeug # wtforms -marshmallow==3.21.2 \ - --hash=sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1 \ - --hash=sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56 +marshmallow==4.2.2 \ + --hash=sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121 \ + --hash=sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f # via # flask-marshmallow # marshmallow-sqlalchemy @@ -468,7 +468,6 @@ packaging==24.0 \ # via # gunicorn # kombu - # marshmallow # pytest # queue-api platformdirs==4.9.4 \ diff --git a/api/uv.lock b/api/uv.lock index ed344cdaf..6191fbbdb 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -766,14 +766,11 @@ wheels = [ [[package]] name = "marshmallow" -version = "3.21.2" +version = "4.2.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/16/06ad266adc423f9d7ee49dce26787b973907aa70213760c9fe1711745405/marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56", size = 176330, upload-time = "2024-05-01T21:10:53.32Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/03/261af5efb3d3ce0e2db3fd1e11dc5a96b74a4fb76e488da1c845a8f12345/marshmallow-4.2.2.tar.gz", hash = "sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f", size = 221404, upload-time = "2026-02-04T15:47:03.401Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/24/cbb242420021a79c87768dcd22ce028f48ef40913239ad6106c8a557f52c/marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1", size = 49299, upload-time = "2024-05-01T21:10:51.063Z" }, + { url = "https://files.pythonhosted.org/packages/aa/70/bb89f807a6a6704bdc4d6f850d5d32954f6c1965e3248e31455defdf2f30/marshmallow-4.2.2-py3-none-any.whl", hash = "sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121", size = 48454, upload-time = "2026-02-04T15:47:02.013Z" }, ] [[package]] @@ -1220,7 +1217,7 @@ requires-dist = [ { name = "lazy-object-proxy", specifier = "==1.10.0" }, { name = "mako", specifier = "==1.3.3" }, { name = "markupsafe", specifier = "==2.1.5" }, - { name = "marshmallow", specifier = "==3.21.2" }, + { name = "marshmallow", specifier = "==4.2.2" }, { name = "marshmallow-sqlalchemy", specifier = "==1.4.2" }, { name = "minio", specifier = "==7.2.20" }, { name = "oauthlib", specifier = "==3.2.2" }, From 97de20e2ca73c8673c58ea68bf6f8e430c02d722 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Thu, 26 Mar 2026 11:29:19 -0700 Subject: [PATCH 19/81] Update remaining api dependencies --- api/app/models/bookings/appointments.py | 6 +- api/app/models/bookings/booking.py | 3 +- api/app/models/theq/citizen.py | 4 +- api/app/resources/theq/slack.py | 1 - .../tests/test_dependency_modernization.py | 8 + api/app/tests/test_sqlalchemy_smoke.py | 79 ++ api/app/utilities/sqlalchemy_compat.py | 47 + api/pyproject.toml | 133 +-- api/requirements.txt | 717 ++++++--------- api/requirements_dev.txt | 653 ++++++-------- api/sqlalchemy_utc.py | 7 + api/uv.lock | 826 ++++++------------ 12 files changed, 969 insertions(+), 1515 deletions(-) create mode 100644 api/app/utilities/sqlalchemy_compat.py create mode 100644 api/sqlalchemy_utc.py diff --git a/api/app/models/bookings/appointments.py b/api/app/models/bookings/appointments.py index 37aa843b4..09c90f127 100644 --- a/api/app/models/bookings/appointments.py +++ b/api/app/models/bookings/appointments.py @@ -14,7 +14,7 @@ from app.models.bookings import Base from qsystem import db -from sqlalchemy_utc import UtcDateTime, utcnow +from app.utilities.sqlalchemy_compat import UtcDateTime, utcnow from sqlalchemy import event, func, or_, and_, text from datetime import datetime, timedelta, timezone from dateutil.parser import parse @@ -39,9 +39,9 @@ class Appointment(Base): recurring_uuid = db.Column(db.String(255), nullable=True) online_flag = db.Column(db.Boolean(), nullable=True, default=False) is_draft = db.Column(db.Boolean(), nullable=True, default=False) - created_at = db.Column(UtcDateTime, nullable=True, default=utcnow()) + created_at = db.Column(UtcDateTime, nullable=True, default=utcnow) stat_flag = db.Column(db.Boolean, default=False, nullable=False) - updated_at = db.Column(UtcDateTime, onupdate=utcnow(), default=None) + updated_at = db.Column(UtcDateTime, onupdate=utcnow, default=None) office = db.relationship("Office") service = db.relationship("Service") diff --git a/api/app/models/bookings/booking.py b/api/app/models/bookings/booking.py index 337e91d45..13932de07 100644 --- a/api/app/models/bookings/booking.py +++ b/api/app/models/bookings/booking.py @@ -14,7 +14,7 @@ from app.models.bookings import Base from qsystem import db -from sqlalchemy_utc import UtcDateTime +from app.utilities.sqlalchemy_compat import UtcDateTime class Booking(Base): @@ -55,4 +55,3 @@ def __repr__(self): def __init__(self, **kwargs): super(Booking, self).__init__(**kwargs) - diff --git a/api/app/models/theq/citizen.py b/api/app/models/theq/citizen.py index 2add822a7..bbff232b2 100644 --- a/api/app/models/theq/citizen.py +++ b/api/app/models/theq/citizen.py @@ -14,7 +14,7 @@ from qsystem import db from app.models.theq import Base -from sqlalchemy_utc import UtcDateTime, utcnow +from app.utilities.sqlalchemy_compat import UtcDateTime, utcnow class Citizen(Base): @@ -50,7 +50,7 @@ class Citizen(Base): start_position = db.Column(db.Integer, nullable=True) # digital signage - created_at = db.Column(UtcDateTime, nullable=True, default=utcnow()) + created_at = db.Column(UtcDateTime, nullable=True, default=utcnow) def __repr__(self): diff --git a/api/app/resources/theq/slack.py b/api/app/resources/theq/slack.py index 55271d429..b9fbd0cef 100644 --- a/api/app/resources/theq/slack.py +++ b/api/app/resources/theq/slack.py @@ -18,7 +18,6 @@ from qsystem import application, api, db, socketio from app.auth import required_scope from app.models.theq import Citizen, CSR -from cockroachdb.sqlalchemy import run_transaction import logging from marshmallow import ValidationError, pre_load from sqlalchemy import exc diff --git a/api/app/tests/test_dependency_modernization.py b/api/app/tests/test_dependency_modernization.py index 28e94d43c..08ddb5e94 100644 --- a/api/app/tests/test_dependency_modernization.py +++ b/api/app/tests/test_dependency_modernization.py @@ -4,6 +4,7 @@ from app.utilities.date_util import add_delta_to_time, current_pacific_time from app.utilities.notification_email import send_email +from app.utilities.sqlalchemy_compat import utcnow from app.utilities.timezone_utils import as_utc, get_timezone, localize @@ -34,6 +35,13 @@ def test_current_pacific_time_uses_vancouver_zone(): assert pacific_now.tzinfo == get_timezone("America/Vancouver") +def test_utcnow_returns_aware_utc_datetime(): + current = utcnow() + + assert current.utcoffset().total_seconds() == 0 + assert current.tzname() == "UTC" + + def test_send_email_posts_json_payload_with_timeout(monkeypatch): app = Flask(__name__) app.config["NOTIFICATIONS_EMAIL_ENDPOINT"] = "https://example.com/email" diff --git a/api/app/tests/test_sqlalchemy_smoke.py b/api/app/tests/test_sqlalchemy_smoke.py index f6bffcad8..31e1c1b49 100644 --- a/api/app/tests/test_sqlalchemy_smoke.py +++ b/api/app/tests/test_sqlalchemy_smoke.py @@ -79,3 +79,82 @@ def test_appointment_crud_and_version_rows(app, db, migrated_database): assert version_count >= 2 assert transaction_count >= 2 + + +def test_local_utc_type_round_trips_booking_appointment_and_citizen(app, db, migrated_database): + from app.models.bookings import Appointment, Booking, Room + from app.models.theq.citizen import Citizen + from app.models.theq.citizen_state import CitizenState + from app.models.theq.office import Office + from app.models.theq.smartboard import SmartBoard + from app.models.theq.timezone import Timezone + + with app.app_context(): + smartboard = SmartBoard(sb_type="callbyticket") + timezone_row = Timezone(timezone_name="Canada/Pacific") + citizen_state = CitizenState(cs_state_name="Waiting", cs_state_desc="Waiting") + db.session.add_all([smartboard, timezone_row, citizen_state]) + db.session.commit() + + office = Office( + office_name="UTC Smoke Office", + office_number=9998, + sb_id=smartboard.sb_id, + exams_enabled_ind=0, + appointments_enabled_ind=1, + timezone_id=timezone_row.timezone_id, + ) + db.session.add(office) + db.session.commit() + + room = Room( + office_id=office.office_id, + room_name="UTC Smoke Room", + capacity=4, + color="blue", + ) + db.session.add(room) + db.session.commit() + + booking = Booking( + office_id=office.office_id, + room_id=room.room_id, + start_time=datetime(2026, 3, 25, 16, 0, tzinfo=timezone.utc), + end_time=datetime(2026, 3, 25, 17, 0, tzinfo=timezone.utc), + ) + citizen = Citizen( + office_id=office.office_id, + cs_id=citizen_state.cs_id, + start_time=datetime(2026, 3, 25, 15, 30, tzinfo=timezone.utc), + ) + appointment = Appointment( + office_id=office.office_id, + start_time=datetime(2026, 3, 25, 18, 0, tzinfo=timezone.utc), + end_time=datetime(2026, 3, 25, 18, 30, tzinfo=timezone.utc), + citizen_name="UTC Smoke Citizen", + contact_information="utc@example.com", + ) + db.session.add_all([booking, citizen, appointment]) + db.session.commit() + + appointment.comments = "updated" + db.session.add(appointment) + db.session.commit() + + booking_id = booking.booking_id + citizen_id = citizen.citizen_id + appointment_id = appointment.appointment_id + + db.session.expunge_all() + + saved_booking = db.session.get(Booking, booking_id) + saved_citizen = db.session.get(Citizen, citizen_id) + saved_appointment = db.session.get(Appointment, appointment_id) + + assert saved_booking.start_time.utcoffset().total_seconds() == 0 + assert saved_booking.end_time.utcoffset().total_seconds() == 0 + assert saved_citizen.created_at.utcoffset().total_seconds() == 0 + assert saved_appointment.start_time.utcoffset().total_seconds() == 0 + assert saved_appointment.end_time.utcoffset().total_seconds() == 0 + assert saved_appointment.created_at.utcoffset().total_seconds() == 0 + assert saved_appointment.updated_at.utcoffset().total_seconds() == 0 diff --git a/api/app/utilities/sqlalchemy_compat.py b/api/app/utilities/sqlalchemy_compat.py new file mode 100644 index 000000000..a57bb4767 --- /dev/null +++ b/api/app/utilities/sqlalchemy_compat.py @@ -0,0 +1,47 @@ +"""Local SQLAlchemy helpers for UTC-aware datetime columns.""" + +from __future__ import annotations + +from datetime import datetime, timezone + +from sqlalchemy import DateTime +from sqlalchemy.types import TypeDecorator + + +UTC = timezone.utc + + +class UtcDateTime(TypeDecorator): + """Store datetimes in UTC and always return aware UTC values.""" + + impl = DateTime + cache_ok = True + + def __init__(self, *args, timezone: bool = True, **kwargs): + super().__init__(*args, **kwargs) + self.timezone = timezone + + def load_dialect_impl(self, dialect): + return dialect.type_descriptor(DateTime(timezone=self.timezone)) + + def process_bind_param(self, value, dialect): + del dialect + if value is None: + return None + if value.tzinfo is None: + return value.replace(tzinfo=UTC) + return value.astimezone(UTC) + + def process_result_value(self, value, dialect): + del dialect + if value is None: + return None + if value.tzinfo is None: + return value.replace(tzinfo=UTC) + return value.astimezone(UTC) + + +def utcnow() -> datetime: + """Return the current UTC time as an aware datetime.""" + + return datetime.now(UTC) diff --git a/api/pyproject.toml b/api/pyproject.toml index f63f84b65..d21204af5 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -8,109 +8,52 @@ version = "0.0.0" description = "Queue management API" requires-python = ">=3.11,<3.12" dependencies = [ - "alembic==1.18.4", - "amqp==5.2.0", - "aniso8601==9.0.1", - "argon2-cffi==25.1.0", - "argon2-cffi-bindings==25.1.0", - "attrs==23.2.0", - "bidict==0.23.1", - "Blinker==1.9.0", - "Brotli==1.1.0", - "cachelib==0.13.0", - "certifi==2024.7.4", - "cffi==2.0.0", - "charset-normalizer==3.3.2", - "click==8.1.7", - "cryptography==46.0.5", - "decorator==5.0.9", - "dill==0.4.1", - "ecdsa==0.19.0", - "filelock==3.0.12", - "Flask==3.1.3", - "Flask-Admin==2.0.2", - "Flask-Caching==2.3.1", - "Flask-Compress==1.17", - "Flask-Cors==5.0.0", - "flask-jwt-oidc==0.8.0", - "Flask-Login==0.6.3", - "flask-marshmallow==1.4.0", - "Flask-Migrate==4.1.0", - "flask-restx==1.3.2", - "Flask-SocketIO==5.6.1", - "Flask-SQLAlchemy==3.1.1", - "gevent==25.9.1", - "greenlet==3.3.2", - "gunicorn==25.2.0", - "h11==0.14.0", - "idna==3.7", - "ijson==2.6.1", - "itsdangerous==2.2.0", - "Jinja2==3.1.6", - "jsonschema==4.22.0", - "jsonschema-specifications==2023.12.1", - "kombu==5.6.2", - "lazy-object-proxy==1.10.0", - "Mako==1.3.3", - "MarkupSafe==2.1.5", - "marshmallow==4.2.2", - "marshmallow-sqlalchemy==1.4.2", - "minio==7.2.20", - "oauthlib==3.2.2", - "packaging==24.0", - "platformdirs==4.9.4", - "psycopg2-binary==2.9.11", - "pyasn1==0.6.0", - "pycparser==3.0", - "pycryptodome==3.23.0", - "Pygments==2.19.2", - "PyJWT==2.12.1", - "pyparsing==3.0.7", - "python-dateutil==2.9.0.post0", - "python-dotenv==1.2.2", - "python-engineio==4.13.1", - "python-jose==3.3.0", - "python-magic==0.4.27", - "python-socketio==5.16.1", - "redis==7.4.0", - "referencing==0.35.1", - "requests==2.32.5", - "requests-oauthlib==1.3.1", - "rpds-py==0.18.1", - "rsa==4.9.1", - "simple-websocket==1.0.0", - "snowplow-tracker==1.1.0", - "SQLAlchemy==2.0.48", - "SQLAlchemy-Utc==0.14.0", - "SQLAlchemy-Utils==0.42.1", - "toml==0.10.2", - "typing_extensions==4.15.0", - "tzdata==2025.3", - "urllib3==2.6.3", - "vine==5.1.0", - "Werkzeug==3.1.6", - "wrapt==1.12.1", - "wsproto==1.2.0", - "WTForms==3.2.1", - "zope.event==5.0", - "zope.interface==6.3", - "zstandard==0.25.0", + "alembic>=1.18,<1.19", + "amqp>=5.3,<5.4", + "click>=8.3,<8.4", + "filelock>=3.25,<3.26", + "Flask>=3.1,<3.2", + "Flask-Admin>=2.0,<2.1", + "Flask-Caching>=2.3,<2.4", + "Flask-Compress>=1.23,<1.24", + "Flask-Cors>=6.0,<6.1", + "flask-jwt-oidc>=0.9,<0.10", + "Flask-Login>=0.6,<0.7", + "flask-marshmallow>=1.4,<1.5", + "Flask-Migrate>=4.1,<4.2", + "flask-restx>=1.3,<1.4", + "Flask-SocketIO>=5.6,<5.7", + "Flask-SQLAlchemy>=3.1,<3.2", + "gevent>=25.9,<25.10", + "gunicorn>=25.2,<25.3", + "Jinja2>=3.1,<3.2", + "kombu>=5.6,<5.7", + "MarkupSafe>=3.0,<3.1", + "marshmallow>=4.2,<4.3", + "marshmallow-sqlalchemy>=1.4,<1.5", + "minio>=7.2,<7.3", + "psycopg2-binary>=2.9,<2.10", + "python-dateutil>=2.9,<2.10", + "python-dotenv>=1.2,<1.3", + "python-jose>=3.5,<3.6", + "redis>=7.4,<7.5", + "requests>=2.33,<2.34", + "snowplow-tracker>=1.1,<1.2", + "SQLAlchemy>=2.0,<2.1", + "WTForms>=3.2,<3.3", ] [dependency-groups] dev = [ - "astroid==4.0.4", - "isort==8.0.1", - "mccabe==0.6.1", - "pluggy==1.6.0", - "pylint==4.0.5", - "pytest==9.0.2", - "pytest-cov==7.0.0", + "isort>=8.0,<8.1", + "pylint>=4.0,<4.1", + "pytest>=9.0,<9.1", + "pytest-cov>=7.1,<7.2", ] [tool.setuptools] include-package-data = true -py-modules = ["config", "manage", "qsystem", "version", "wsgi"] +py-modules = ["config", "manage", "qsystem", "sqlalchemy_utc", "version", "wsgi"] [tool.setuptools.packages.find] include = ["app*"] diff --git a/api/requirements.txt b/api/requirements.txt index ec0cd0e43..b4f0c9470 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,29 +1,25 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --format requirements-txt --no-emit-project --output-file requirements.txt +# uv export --frozen --no-dev --format requirements.txt --no-emit-project --output-file requirements.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc # via # flask-migrate # queue-api -amqp==5.2.0 \ - --hash=sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637 \ - --hash=sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd +amqp==5.3.1 \ + --hash=sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2 \ + --hash=sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432 # via # kombu # queue-api -aniso8601==9.0.1 \ - --hash=sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f \ - --hash=sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973 - # via - # flask-restx - # queue-api +aniso8601==10.0.1 \ + --hash=sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845 \ + --hash=sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e + # via flask-restx argon2-cffi==25.1.0 \ --hash=sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1 \ --hash=sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 - # via - # minio - # queue-api + # via minio argon2-cffi-bindings==25.1.0 \ --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ @@ -36,58 +32,66 @@ argon2-cffi-bindings==25.1.0 \ --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a - # via - # argon2-cffi - # queue-api -astroid==4.0.4 \ - --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ - --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 - # via pylint + # via argon2-cffi async-timeout==5.0.1 ; python_full_version < '3.11.3' \ --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 # via redis -attrs==23.2.0 \ - --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ - --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # jsonschema - # queue-api # referencing +backports-zstd==1.3.0 \ + --hash=sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e \ + --hash=sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f \ + --hash=sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5 \ + --hash=sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d \ + --hash=sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9 \ + --hash=sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a \ + --hash=sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120 \ + --hash=sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5 \ + --hash=sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b \ + --hash=sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3 \ + --hash=sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9 \ + --hash=sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c \ + --hash=sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72 \ + --hash=sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3 \ + --hash=sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c \ + --hash=sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f \ + --hash=sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957 \ + --hash=sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533 \ + --hash=sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2 \ + --hash=sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4 \ + --hash=sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a \ + --hash=sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943 \ + --hash=sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2 \ + --hash=sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1 + # via flask-compress bidict==0.23.1 \ --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 - # via - # python-socketio - # queue-api + # via python-socketio blinker==1.9.0 \ --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc # via # flask # flask-socketio - # queue-api -brotli==1.1.0 \ - --hash=sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9 \ - --hash=sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757 \ - --hash=sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0 \ - --hash=sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd \ - --hash=sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50 \ - --hash=sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265 \ - --hash=sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327 \ - --hash=sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd \ - --hash=sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724 \ - --hash=sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8 \ - --hash=sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc \ - --hash=sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61 \ - --hash=sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1 \ - --hash=sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f \ - --hash=sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6 \ - --hash=sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf \ - --hash=sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b - # via - # flask-compress - # queue-api +brotli==1.2.0 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ + --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ + --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ + --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ + --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ + --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ + --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ + --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ + --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ + --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a + # via flask-compress brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ @@ -106,13 +110,11 @@ cachelib==0.13.0 \ # via # flask-caching # flask-jwt-oidc - # queue-api -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 # via # minio - # queue-api # requests cffi==2.0.0 \ --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ @@ -134,31 +136,29 @@ cffi==2.0.0 \ # brotlicffi # cryptography # gevent - # queue-api -charset-normalizer==3.3.2 \ - --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ - --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ - --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ - --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ - --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ - --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ - --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ - --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ - --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ - --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ - --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ - --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ - --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ - --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ - --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ - --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ - --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 - # via - # queue-api - # requests -click==8.1.7 \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de +charset-normalizer==3.4.6 \ + --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ + --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ + --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ + --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ + --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ + --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ + --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ + --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ + --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ + --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ + --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ + --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ + --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ + --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ + --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ + --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ + --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ + --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed + # via requests +click==8.3.1 \ + --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ + --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 # via # flask # flask-socketio @@ -166,92 +166,52 @@ click==8.1.7 \ colorama==0.4.6 ; sys_platform == 'win32' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via - # click - # pylint - # pytest -coverage==7.13.5 \ - --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \ - --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \ - --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \ - --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \ - --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \ - --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ - --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \ - --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \ - --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \ - --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \ - --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \ - --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \ - --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \ - --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \ - --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ - --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ - --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b - # via pytest-cov -cryptography==46.0.5 \ - --hash=sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72 \ - --hash=sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235 \ - --hash=sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356 \ - --hash=sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257 \ - --hash=sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad \ - --hash=sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4 \ - --hash=sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c \ - --hash=sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614 \ - --hash=sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed \ - --hash=sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31 \ - --hash=sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229 \ - --hash=sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0 \ - --hash=sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731 \ - --hash=sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b \ - --hash=sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263 \ - --hash=sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595 \ - --hash=sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1 \ - --hash=sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48 \ - --hash=sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76 \ - --hash=sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18 \ - --hash=sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d \ - --hash=sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1 \ - --hash=sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7 \ - --hash=sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82 \ - --hash=sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4 \ - --hash=sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c \ - --hash=sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d \ - --hash=sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a \ - --hash=sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a \ - --hash=sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d \ - --hash=sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b \ - --hash=sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9 \ - --hash=sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da \ - --hash=sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2 \ - --hash=sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2 - # via - # flask-jwt-oidc - # queue-api -decorator==5.0.9 \ - --hash=sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323 \ - --hash=sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5 - # via queue-api -dill==0.4.1 \ - --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ - --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa - # via - # pylint - # queue-api -ecdsa==0.19.0 \ - --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ - --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 - # via - # python-jose - # queue-api -filelock==3.0.12 \ - --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ - --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 + # via click +cryptography==46.0.6 \ + --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ + --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ + --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ + --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ + --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ + --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ + --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ + --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ + --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ + --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ + --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ + --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ + --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ + --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ + --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ + --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ + --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ + --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ + --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ + --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ + --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ + --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ + --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ + --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ + --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ + --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ + --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ + --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 + # via flask-jwt-oidc +ecdsa==0.19.2 \ + --hash=sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930 \ + --hash=sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399 + # via python-jose +filelock==3.25.2 \ + --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ + --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 # via queue-api -flake8-import-order==0.19.2 \ - --hash=sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2 \ - --hash=sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c - # via zimports flask==3.1.3 \ --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c @@ -276,17 +236,17 @@ flask-caching==2.3.1 \ --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 # via queue-api -flask-compress==1.17 \ - --hash=sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8 \ - --hash=sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20 +flask-compress==1.23 \ + --hash=sha256:52108afb4d133a5aab9809e6ac3c085ed7b9c788c75c6846c129faa28468f08c \ + --hash=sha256:5580935b422e3f136b9a90909e4b1015ac2b29c9aebe0f8733b790fde461c545 # via queue-api -flask-cors==5.0.0 \ - --hash=sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef \ - --hash=sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc +flask-cors==6.0.2 \ + --hash=sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423 \ + --hash=sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a # via queue-api -flask-jwt-oidc==0.8.0 \ - --hash=sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0 \ - --hash=sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4 +flask-jwt-oidc==0.9.0 \ + --hash=sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3 \ + --hash=sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c # via queue-api flask-login==0.6.3 \ --hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \ @@ -324,7 +284,7 @@ gevent==25.9.1 \ --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 # via queue-api -greenlet==3.3.2 \ +greenlet==3.3.2 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or platform_python_implementation == 'CPython' \ --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ @@ -337,45 +297,27 @@ greenlet==3.3.2 \ --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 # via # gevent - # queue-api # sqlalchemy gunicorn==25.2.0 \ --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc # via queue-api -h11==0.14.0 \ - --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ - --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 - # via - # queue-api - # wsproto -idna==3.7 \ - --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ - --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 - # via - # queue-api - # requests -ijson==2.6.1 \ - --hash=sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0 - # via queue-api +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via wsproto +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests importlib-resources==6.5.2 \ --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec # via flask-restx -iniconfig==2.3.0 \ - --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ - --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 - # via pytest -isort==8.0.1 \ - --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ - --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 - # via pylint itsdangerous==2.2.0 \ --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 - # via - # flask - # queue-api + # via flask jinja2==3.1.6 \ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 @@ -384,51 +326,35 @@ jinja2==3.1.6 \ # flask-admin # flask-socketio # queue-api -jsonschema==4.22.0 \ - --hash=sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7 \ - --hash=sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802 - # via - # flask-restx - # queue-api -jsonschema-specifications==2023.12.1 \ - --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ - --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c - # via - # jsonschema - # queue-api +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via flask-restx +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema kombu==5.6.2 \ --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \ --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93 # via queue-api -lazy-object-proxy==1.10.0 \ - --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \ - --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \ - --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \ - --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \ - --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \ - --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \ - --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \ - --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \ - --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c - # via queue-api -mako==1.3.3 \ - --hash=sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40 \ - --hash=sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73 - # via - # alembic - # queue-api -markupsafe==2.1.5 \ - --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ - --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ - --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ - --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ - --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ - --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ - --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ - --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ - --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ - --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ - --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b +mako==1.3.10 \ + --hash=sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28 \ + --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 + # via alembic +markupsafe==3.0.3 \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a # via # flask # flask-admin @@ -437,9 +363,9 @@ markupsafe==2.1.5 \ # queue-api # werkzeug # wtforms -marshmallow==4.2.2 \ - --hash=sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121 \ - --hash=sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f +marshmallow==4.2.3 \ + --hash=sha256:3e3fef6b3603721a25a723b8caedfa488369bddaf9bc03b40b9442c90aebd22b \ + --hash=sha256:c84fd89817ecea690bde1eb925036070e5e9883148217585adc56d5cfbc082b8 # via # flask-marshmallow # marshmallow-sqlalchemy @@ -448,40 +374,16 @@ marshmallow-sqlalchemy==1.4.2 \ --hash=sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c \ --hash=sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40 # via queue-api -mccabe==0.6.1 \ - --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ - --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f - # via pylint minio==7.2.20 \ --hash=sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598 \ --hash=sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e # via queue-api -oauthlib==3.2.2 \ - --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ - --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 - # via - # queue-api - # requests-oauthlib -packaging==24.0 \ - --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ - --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 # via # gunicorn # kombu - # pytest - # queue-api -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 - # via - # pylint - # queue-api -pluggy==1.6.0 \ - --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ - --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 - # via - # pytest - # pytest-cov psycopg2-binary==2.9.11 \ --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \ --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \ @@ -496,23 +398,16 @@ psycopg2-binary==2.9.11 \ --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 # via queue-api -pyasn1==0.6.0 \ - --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ - --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 +pyasn1==0.6.3 \ + --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ + --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde # via # python-jose - # queue-api # rsa -pycodestyle==2.14.0 \ - --hash=sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783 \ - --hash=sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d - # via flake8-import-order -pycparser==3.0 \ +pycparser==3.0 ; implementation_name != 'PyPy' \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 - # via - # cffi - # queue-api + # via cffi pycryptodome==3.23.0 \ --hash=sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c \ --hash=sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f \ @@ -526,39 +421,11 @@ pycryptodome==3.23.0 \ --hash=sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575 \ --hash=sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843 \ --hash=sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa - # via - # minio - # queue-api -pyflakes==3.4.0 \ - --hash=sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58 \ - --hash=sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f - # via zimports -pygments==2.19.2 \ - --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ - --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b - # via - # pytest - # queue-api + # via minio pyjwt==2.12.1 \ --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b - # via - # flask-jwt-oidc - # queue-api -pylint==4.0.5 \ - --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ - --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c -pyparsing==3.0.7 \ - --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \ - --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484 - # via queue-api -pytest==9.0.2 \ - --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ - --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 - # via pytest-cov -pytest-cov==7.0.0 \ - --hash=sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1 \ - --hash=sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 + # via flask-jwt-oidc python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 @@ -570,91 +437,77 @@ python-dotenv==1.2.2 \ python-engineio==4.13.1 \ --hash=sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066 \ --hash=sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399 - # via - # python-socketio - # queue-api -python-jose==3.3.0 \ - --hash=sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a \ - --hash=sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a - # via queue-api -python-magic==0.4.27 \ - --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ - --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 + # via python-socketio +python-jose==3.5.0 \ + --hash=sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771 \ + --hash=sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b # via queue-api python-socketio==5.16.1 \ --hash=sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35 \ --hash=sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89 - # via - # flask-socketio - # queue-api + # via flask-socketio redis==7.4.0 \ --hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \ --hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec # via queue-api -referencing==0.35.1 \ - --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ - --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 # via # flask-restx # jsonschema # jsonschema-specifications - # queue-api -requests==2.32.5 \ - --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ - --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf +requests==2.33.0 \ + --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ + --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 # via # queue-api - # requests-oauthlib # snowplow-tracker -requests-oauthlib==1.3.1 \ - --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ - --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a - # via queue-api -rpds-py==0.18.1 \ - --hash=sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc \ - --hash=sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7 \ - --hash=sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7 \ - --hash=sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100 \ - --hash=sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261 \ - --hash=sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8 \ - --hash=sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2 \ - --hash=sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d \ - --hash=sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07 \ - --hash=sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8 \ - --hash=sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88 \ - --hash=sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb \ - --hash=sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f \ - --hash=sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e +rpds-py==0.30.0 \ + --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \ + --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \ + --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \ + --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \ + --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \ + --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \ + --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \ + --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \ + --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \ + --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \ + --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \ + --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \ + --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \ + --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \ + --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \ + --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \ + --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \ + --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \ + --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \ + --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \ + --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \ + --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \ + --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \ + --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \ + --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ + --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \ + --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \ + --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a # via # jsonschema - # queue-api # referencing rsa==4.9.1 \ --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 - # via - # python-jose - # queue-api -setuptools==82.0.1 \ - --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \ - --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb - # via - # flake8-import-order - # sqlalchemy-utc - # zope-event - # zope-interface -simple-websocket==1.0.0 \ - --hash=sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8 \ - --hash=sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc - # via - # python-engineio - # queue-api + # via python-jose +simple-websocket==1.1.0 \ + --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ + --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 + # via python-engineio six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via # ecdsa - # flask-jwt-oidc # python-dateutil snowplow-tracker==1.1.0 \ --hash=sha256:24ea32ddac9cca547421bf9ab162f5f33c00711c6ef118ad5f78093cee962224 \ @@ -675,60 +528,24 @@ sqlalchemy==2.0.48 \ # flask-sqlalchemy # marshmallow-sqlalchemy # queue-api - # sqlalchemy-utc - # sqlalchemy-utils -sqlalchemy-utc==0.14.0 \ - --hash=sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa \ - --hash=sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b - # via queue-api -sqlalchemy-utils==0.42.1 \ - --hash=sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80 \ - --hash=sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e - # via queue-api -toml==0.10.2 \ - --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ - --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f - # via queue-api -tomli==2.4.1 \ - --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ - --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ - --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ - --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ - --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ - --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ - --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ - --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ - --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ - --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ - --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 - # via - # coverage - # zimports -tomlkit==0.14.0 \ - --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ - --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 - # via pylint typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 # via # alembic # minio - # queue-api + # referencing # snowplow-tracker # sqlalchemy tzdata==2025.3 \ --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 - # via - # kombu - # queue-api + # via kombu urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 # via # minio - # queue-api # requests vine==5.1.0 \ --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \ @@ -736,72 +553,36 @@ vine==5.1.0 \ # via # amqp # kombu - # queue-api -werkzeug==3.1.6 \ - --hash=sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25 \ - --hash=sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131 +werkzeug==3.1.7 \ + --hash=sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f \ + --hash=sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351 # via # flask # flask-admin + # flask-cors # flask-login # flask-restx # flask-socketio - # queue-api -wrapt==1.12.1 \ - --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 - # via queue-api -wsproto==1.2.0 \ - --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ - --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 - # via - # queue-api - # simple-websocket +wsproto==1.3.2 \ + --hash=sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584 \ + --hash=sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294 + # via simple-websocket wtforms==3.2.1 \ --hash=sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4 \ --hash=sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682 # via # flask-admin # queue-api -zimports==0.6.3 \ - --hash=sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6 \ - --hash=sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe - # via flask-jwt-oidc -zope-event==5.0 \ - --hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \ - --hash=sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd - # via - # gevent - # queue-api -zope-interface==6.3 \ - --hash=sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130 \ - --hash=sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10 \ - --hash=sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299 \ - --hash=sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39 \ - --hash=sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061 \ - --hash=sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e \ - --hash=sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a - # via - # gevent - # queue-api -zstandard==0.25.0 \ - --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ - --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ - --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ - --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ - --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ - --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ - --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ - --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ - --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ - --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ - --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ - --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ - --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ - --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ - --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ - --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ - --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ - --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 - # via - # flask-compress - # queue-api +zope-event==6.1 \ + --hash=sha256:0ca78b6391b694272b23ec1335c0294cc471065ed10f7f606858fc54566c25a0 \ + --hash=sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0 + # via gevent +zope-interface==8.2 \ + --hash=sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489 \ + --hash=sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c \ + --hash=sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b \ + --hash=sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224 \ + --hash=sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322 \ + --hash=sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce \ + --hash=sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466 + # via gevent diff --git a/api/requirements_dev.txt b/api/requirements_dev.txt index 5bb52208c..d59e62c36 100644 --- a/api/requirements_dev.txt +++ b/api/requirements_dev.txt @@ -1,29 +1,25 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt +# uv export --frozen --group dev --format requirements.txt --no-emit-project --output-file requirements_dev.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc # via # flask-migrate # queue-api -amqp==5.2.0 \ - --hash=sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637 \ - --hash=sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd +amqp==5.3.1 \ + --hash=sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2 \ + --hash=sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432 # via # kombu # queue-api -aniso8601==9.0.1 \ - --hash=sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f \ - --hash=sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973 - # via - # flask-restx - # queue-api +aniso8601==10.0.1 \ + --hash=sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845 \ + --hash=sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e + # via flask-restx argon2-cffi==25.1.0 \ --hash=sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1 \ --hash=sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 - # via - # minio - # queue-api + # via minio argon2-cffi-bindings==25.1.0 \ --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ @@ -36,9 +32,7 @@ argon2-cffi-bindings==25.1.0 \ --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a - # via - # argon2-cffi - # queue-api + # via argon2-cffi astroid==4.0.4 \ --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 @@ -47,47 +41,61 @@ async-timeout==5.0.1 ; python_full_version < '3.11.3' \ --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 # via redis -attrs==23.2.0 \ - --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ - --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # jsonschema - # queue-api # referencing +backports-zstd==1.3.0 \ + --hash=sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e \ + --hash=sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f \ + --hash=sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5 \ + --hash=sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d \ + --hash=sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9 \ + --hash=sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a \ + --hash=sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120 \ + --hash=sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5 \ + --hash=sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b \ + --hash=sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3 \ + --hash=sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9 \ + --hash=sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c \ + --hash=sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72 \ + --hash=sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3 \ + --hash=sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c \ + --hash=sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f \ + --hash=sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957 \ + --hash=sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533 \ + --hash=sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2 \ + --hash=sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4 \ + --hash=sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a \ + --hash=sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943 \ + --hash=sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2 \ + --hash=sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1 + # via flask-compress bidict==0.23.1 \ --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 - # via - # python-socketio - # queue-api + # via python-socketio blinker==1.9.0 \ --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc # via # flask # flask-socketio - # queue-api -brotli==1.1.0 \ - --hash=sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9 \ - --hash=sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757 \ - --hash=sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0 \ - --hash=sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd \ - --hash=sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50 \ - --hash=sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265 \ - --hash=sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327 \ - --hash=sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd \ - --hash=sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724 \ - --hash=sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8 \ - --hash=sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc \ - --hash=sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61 \ - --hash=sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1 \ - --hash=sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f \ - --hash=sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6 \ - --hash=sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf \ - --hash=sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b - # via - # flask-compress - # queue-api +brotli==1.2.0 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ + --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ + --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ + --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ + --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ + --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ + --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ + --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ + --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ + --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a + # via flask-compress brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ @@ -106,13 +114,11 @@ cachelib==0.13.0 \ # via # flask-caching # flask-jwt-oidc - # queue-api -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 # via # minio - # queue-api # requests cffi==2.0.0 \ --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ @@ -134,31 +140,29 @@ cffi==2.0.0 \ # brotlicffi # cryptography # gevent - # queue-api -charset-normalizer==3.3.2 \ - --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ - --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ - --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ - --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ - --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ - --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ - --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ - --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ - --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ - --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ - --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ - --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ - --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ - --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ - --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ - --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ - --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 - # via - # queue-api - # requests -click==8.1.7 \ - --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ - --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de +charset-normalizer==3.4.6 \ + --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ + --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ + --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ + --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ + --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ + --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ + --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ + --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ + --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ + --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ + --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ + --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ + --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ + --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ + --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ + --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ + --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ + --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed + # via requests +click==8.3.1 \ + --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ + --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 # via # flask # flask-socketio @@ -189,69 +193,55 @@ coverage==7.13.5 \ --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b # via pytest-cov -cryptography==46.0.5 \ - --hash=sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72 \ - --hash=sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235 \ - --hash=sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356 \ - --hash=sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257 \ - --hash=sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad \ - --hash=sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4 \ - --hash=sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c \ - --hash=sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614 \ - --hash=sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed \ - --hash=sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31 \ - --hash=sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229 \ - --hash=sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0 \ - --hash=sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731 \ - --hash=sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b \ - --hash=sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263 \ - --hash=sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595 \ - --hash=sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1 \ - --hash=sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48 \ - --hash=sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76 \ - --hash=sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18 \ - --hash=sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d \ - --hash=sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1 \ - --hash=sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7 \ - --hash=sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82 \ - --hash=sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4 \ - --hash=sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c \ - --hash=sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d \ - --hash=sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a \ - --hash=sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a \ - --hash=sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d \ - --hash=sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b \ - --hash=sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9 \ - --hash=sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da \ - --hash=sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2 \ - --hash=sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2 - # via - # flask-jwt-oidc - # queue-api -decorator==5.0.9 \ - --hash=sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323 \ - --hash=sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5 - # via queue-api +cryptography==46.0.6 \ + --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ + --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ + --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ + --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ + --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ + --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ + --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ + --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ + --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ + --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ + --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ + --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ + --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ + --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ + --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ + --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ + --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ + --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ + --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ + --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ + --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ + --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ + --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ + --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ + --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ + --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ + --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ + --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 + # via flask-jwt-oidc dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa - # via - # pylint - # queue-api -ecdsa==0.19.0 \ - --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ - --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 - # via - # python-jose - # queue-api -filelock==3.0.12 \ - --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ - --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 + # via pylint +ecdsa==0.19.2 \ + --hash=sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930 \ + --hash=sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399 + # via python-jose +filelock==3.25.2 \ + --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ + --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 # via queue-api -flake8-import-order==0.19.2 \ - --hash=sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2 \ - --hash=sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c - # via zimports flask==3.1.3 \ --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c @@ -276,17 +266,17 @@ flask-caching==2.3.1 \ --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 # via queue-api -flask-compress==1.17 \ - --hash=sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8 \ - --hash=sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20 +flask-compress==1.23 \ + --hash=sha256:52108afb4d133a5aab9809e6ac3c085ed7b9c788c75c6846c129faa28468f08c \ + --hash=sha256:5580935b422e3f136b9a90909e4b1015ac2b29c9aebe0f8733b790fde461c545 # via queue-api -flask-cors==5.0.0 \ - --hash=sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef \ - --hash=sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc +flask-cors==6.0.2 \ + --hash=sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423 \ + --hash=sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a # via queue-api -flask-jwt-oidc==0.8.0 \ - --hash=sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0 \ - --hash=sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4 +flask-jwt-oidc==0.9.0 \ + --hash=sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3 \ + --hash=sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c # via queue-api flask-login==0.6.3 \ --hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \ @@ -324,7 +314,7 @@ gevent==25.9.1 \ --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 # via queue-api -greenlet==3.3.2 \ +greenlet==3.3.2 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or platform_python_implementation == 'CPython' \ --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ @@ -337,27 +327,19 @@ greenlet==3.3.2 \ --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 # via # gevent - # queue-api # sqlalchemy gunicorn==25.2.0 \ --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc # via queue-api -h11==0.14.0 \ - --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ - --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 - # via - # queue-api - # wsproto -idna==3.7 \ - --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ - --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 - # via - # queue-api - # requests -ijson==2.6.1 \ - --hash=sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0 - # via queue-api +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via wsproto +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests importlib-resources==6.5.2 \ --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec @@ -373,9 +355,7 @@ isort==8.0.1 \ itsdangerous==2.2.0 \ --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 - # via - # flask - # queue-api + # via flask jinja2==3.1.6 \ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 @@ -384,51 +364,35 @@ jinja2==3.1.6 \ # flask-admin # flask-socketio # queue-api -jsonschema==4.22.0 \ - --hash=sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7 \ - --hash=sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802 - # via - # flask-restx - # queue-api -jsonschema-specifications==2023.12.1 \ - --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ - --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c - # via - # jsonschema - # queue-api +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via flask-restx +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema kombu==5.6.2 \ --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \ --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93 # via queue-api -lazy-object-proxy==1.10.0 \ - --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \ - --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \ - --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \ - --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \ - --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \ - --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \ - --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \ - --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \ - --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c - # via queue-api -mako==1.3.3 \ - --hash=sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40 \ - --hash=sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73 - # via - # alembic - # queue-api -markupsafe==2.1.5 \ - --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ - --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ - --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ - --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ - --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ - --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ - --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ - --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ - --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ - --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ - --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b +mako==1.3.10 \ + --hash=sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28 \ + --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 + # via alembic +markupsafe==3.0.3 \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a # via # flask # flask-admin @@ -437,9 +401,9 @@ markupsafe==2.1.5 \ # queue-api # werkzeug # wtforms -marshmallow==4.2.2 \ - --hash=sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121 \ - --hash=sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f +marshmallow==4.2.3 \ + --hash=sha256:3e3fef6b3603721a25a723b8caedfa488369bddaf9bc03b40b9442c90aebd22b \ + --hash=sha256:c84fd89817ecea690bde1eb925036070e5e9883148217585adc56d5cfbc082b8 # via # flask-marshmallow # marshmallow-sqlalchemy @@ -448,34 +412,25 @@ marshmallow-sqlalchemy==1.4.2 \ --hash=sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c \ --hash=sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40 # via queue-api -mccabe==0.6.1 \ - --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ - --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via pylint minio==7.2.20 \ --hash=sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598 \ --hash=sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e # via queue-api -oauthlib==3.2.2 \ - --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ - --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 - # via - # queue-api - # requests-oauthlib -packaging==24.0 \ - --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ - --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 # via # gunicorn # kombu # pytest - # queue-api platformdirs==4.9.4 \ --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 - # via - # pylint - # queue-api + # via pylint pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 @@ -496,23 +451,16 @@ psycopg2-binary==2.9.11 \ --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 # via queue-api -pyasn1==0.6.0 \ - --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ - --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 +pyasn1==0.6.3 \ + --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ + --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde # via # python-jose - # queue-api # rsa -pycodestyle==2.14.0 \ - --hash=sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783 \ - --hash=sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d - # via flake8-import-order -pycparser==3.0 \ +pycparser==3.0 ; implementation_name != 'PyPy' \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 - # via - # cffi - # queue-api + # via cffi pycryptodome==3.23.0 \ --hash=sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c \ --hash=sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f \ @@ -526,39 +474,25 @@ pycryptodome==3.23.0 \ --hash=sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575 \ --hash=sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843 \ --hash=sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa - # via - # minio - # queue-api -pyflakes==3.4.0 \ - --hash=sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58 \ - --hash=sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f - # via zimports + # via minio pygments==2.19.2 \ --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b - # via - # pytest - # queue-api + # via pytest pyjwt==2.12.1 \ --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b - # via - # flask-jwt-oidc - # queue-api + # via flask-jwt-oidc pylint==4.0.5 \ --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c -pyparsing==3.0.7 \ - --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \ - --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484 - # via queue-api pytest==9.0.2 \ --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 # via pytest-cov -pytest-cov==7.0.0 \ - --hash=sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1 \ - --hash=sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 +pytest-cov==7.1.0 \ + --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ + --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 @@ -570,91 +504,77 @@ python-dotenv==1.2.2 \ python-engineio==4.13.1 \ --hash=sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066 \ --hash=sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399 - # via - # python-socketio - # queue-api -python-jose==3.3.0 \ - --hash=sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a \ - --hash=sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a - # via queue-api -python-magic==0.4.27 \ - --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ - --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 + # via python-socketio +python-jose==3.5.0 \ + --hash=sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771 \ + --hash=sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b # via queue-api python-socketio==5.16.1 \ --hash=sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35 \ --hash=sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89 - # via - # flask-socketio - # queue-api + # via flask-socketio redis==7.4.0 \ --hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \ --hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec # via queue-api -referencing==0.35.1 \ - --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ - --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 # via # flask-restx # jsonschema # jsonschema-specifications - # queue-api -requests==2.32.5 \ - --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ - --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf +requests==2.33.0 \ + --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ + --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 # via # queue-api - # requests-oauthlib # snowplow-tracker -requests-oauthlib==1.3.1 \ - --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ - --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a - # via queue-api -rpds-py==0.18.1 \ - --hash=sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc \ - --hash=sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7 \ - --hash=sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7 \ - --hash=sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100 \ - --hash=sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261 \ - --hash=sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8 \ - --hash=sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2 \ - --hash=sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d \ - --hash=sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07 \ - --hash=sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8 \ - --hash=sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88 \ - --hash=sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb \ - --hash=sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f \ - --hash=sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e +rpds-py==0.30.0 \ + --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \ + --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \ + --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \ + --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \ + --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \ + --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \ + --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \ + --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \ + --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \ + --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \ + --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \ + --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \ + --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \ + --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \ + --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \ + --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \ + --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \ + --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \ + --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \ + --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \ + --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \ + --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \ + --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \ + --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \ + --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ + --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \ + --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \ + --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a # via # jsonschema - # queue-api # referencing rsa==4.9.1 \ --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 - # via - # python-jose - # queue-api -setuptools==82.0.1 \ - --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \ - --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb - # via - # flake8-import-order - # sqlalchemy-utc - # zope-event - # zope-interface -simple-websocket==1.0.0 \ - --hash=sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8 \ - --hash=sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc - # via - # python-engineio - # queue-api + # via python-jose +simple-websocket==1.1.0 \ + --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ + --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 + # via python-engineio six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via # ecdsa - # flask-jwt-oidc # python-dateutil snowplow-tracker==1.1.0 \ --hash=sha256:24ea32ddac9cca547421bf9ab162f5f33c00711c6ef118ad5f78093cee962224 \ @@ -675,21 +595,7 @@ sqlalchemy==2.0.48 \ # flask-sqlalchemy # marshmallow-sqlalchemy # queue-api - # sqlalchemy-utc - # sqlalchemy-utils -sqlalchemy-utc==0.14.0 \ - --hash=sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa \ - --hash=sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b - # via queue-api -sqlalchemy-utils==0.42.1 \ - --hash=sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80 \ - --hash=sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e - # via queue-api -toml==0.10.2 \ - --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ - --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f - # via queue-api -tomli==2.4.1 \ +tomli==2.4.1 ; python_full_version <= '3.11' \ --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ @@ -701,9 +607,7 @@ tomli==2.4.1 \ --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 - # via - # coverage - # zimports + # via coverage tomlkit==0.14.0 \ --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 @@ -714,21 +618,18 @@ typing-extensions==4.15.0 \ # via # alembic # minio - # queue-api + # referencing # snowplow-tracker # sqlalchemy tzdata==2025.3 \ --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 - # via - # kombu - # queue-api + # via kombu urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 # via # minio - # queue-api # requests vine==5.1.0 \ --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \ @@ -736,72 +637,36 @@ vine==5.1.0 \ # via # amqp # kombu - # queue-api -werkzeug==3.1.6 \ - --hash=sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25 \ - --hash=sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131 +werkzeug==3.1.7 \ + --hash=sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f \ + --hash=sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351 # via # flask # flask-admin + # flask-cors # flask-login # flask-restx # flask-socketio - # queue-api -wrapt==1.12.1 \ - --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 - # via queue-api -wsproto==1.2.0 \ - --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ - --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 - # via - # queue-api - # simple-websocket +wsproto==1.3.2 \ + --hash=sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584 \ + --hash=sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294 + # via simple-websocket wtforms==3.2.1 \ --hash=sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4 \ --hash=sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682 # via # flask-admin # queue-api -zimports==0.6.3 \ - --hash=sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6 \ - --hash=sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe - # via flask-jwt-oidc -zope-event==5.0 \ - --hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \ - --hash=sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd - # via - # gevent - # queue-api -zope-interface==6.3 \ - --hash=sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130 \ - --hash=sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10 \ - --hash=sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299 \ - --hash=sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39 \ - --hash=sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061 \ - --hash=sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e \ - --hash=sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a - # via - # gevent - # queue-api -zstandard==0.25.0 \ - --hash=sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a \ - --hash=sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6 \ - --hash=sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431 \ - --hash=sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137 \ - --hash=sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa \ - --hash=sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc \ - --hash=sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097 \ - --hash=sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7 \ - --hash=sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b \ - --hash=sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072 \ - --hash=sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c \ - --hash=sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065 \ - --hash=sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f \ - --hash=sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277 \ - --hash=sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778 \ - --hash=sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2 \ - --hash=sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313 \ - --hash=sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4 - # via - # flask-compress - # queue-api +zope-event==6.1 \ + --hash=sha256:0ca78b6391b694272b23ec1335c0294cc471065ed10f7f606858fc54566c25a0 \ + --hash=sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0 + # via gevent +zope-interface==8.2 \ + --hash=sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489 \ + --hash=sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c \ + --hash=sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b \ + --hash=sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224 \ + --hash=sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322 \ + --hash=sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce \ + --hash=sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466 + # via gevent diff --git a/api/sqlalchemy_utc.py b/api/sqlalchemy_utc.py new file mode 100644 index 000000000..d3738a8fc --- /dev/null +++ b/api/sqlalchemy_utc.py @@ -0,0 +1,7 @@ +"""Compatibility shim for legacy Alembic migrations and local model imports.""" + +from app.utilities.sqlalchemy_compat import UtcDateTime, utcnow + + +class sqltypes: + UtcDateTime = UtcDateTime diff --git a/api/uv.lock b/api/uv.lock index 6191fbbdb..1f02705dd 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1,10 +1,6 @@ version = 1 revision = 3 requires-python = "==3.11.*" -resolution-markers = [ - "platform_python_implementation != 'PyPy'", - "platform_python_implementation == 'PyPy'", -] [[package]] name = "alembic" @@ -22,23 +18,23 @@ wheels = [ [[package]] name = "amqp" -version = "5.2.0" +version = "5.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "vine" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/2c/6eb09fbdeb3c060b37bd33f8873832897a83e7a428afe01aad333fc405ec/amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd", size = 128754, upload-time = "2023-11-06T04:54:20.099Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432", size = 129013, upload-time = "2024-11-12T19:55:44.051Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/f0/8e5be5d5e0653d9e1d02b1144efa33ff7d2963dfad07049e02c0fa9b2e8d/amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637", size = 50917, upload-time = "2023-11-06T04:54:08.603Z" }, + { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944, upload-time = "2024-11-12T19:55:41.782Z" }, ] [[package]] name = "aniso8601" -version = "9.0.1" +version = "10.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973", size = 47345, upload-time = "2021-03-02T01:33:22.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/52179c4e3f1978d3d9a285f98c706642522750ef343e9738286130423730/aniso8601-10.0.1.tar.gz", hash = "sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845", size = 47190, upload-time = "2025-04-18T17:29:42.995Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/04/e97c12dc034791d7b504860acfcdd2963fa21ae61eaca1c9d31245f812c3/aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f", size = 52754, upload-time = "2021-03-02T01:33:20.669Z" }, + { url = "https://files.pythonhosted.org/packages/59/75/e0e10dc7ed1408c28e03a6cb2d7a407f99320eb953f229d008a7a6d05546/aniso8601-10.0.1-py2.py3-none-any.whl", hash = "sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e", size = 52848, upload-time = "2025-04-18T17:29:41.492Z" }, ] [[package]] @@ -94,11 +90,42 @@ wheels = [ [[package]] name = "attrs" -version = "23.2.0" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "backports-zstd" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820, upload-time = "2023-12-31T06:30:32.926Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/36a5182ce1d8ef9ef32bff69037bd28b389bbdb66338f8069e61da7028cb/backports_zstd-1.3.0.tar.gz", hash = "sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2", size = 997138, upload-time = "2025-12-29T17:28:06.143Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752, upload-time = "2023-12-31T06:30:30.772Z" }, + { url = "https://files.pythonhosted.org/packages/ac/28/ed31a0e35feb4538a996348362051b52912d50f00d25c2d388eccef9242c/backports_zstd-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9", size = 435660, upload-time = "2025-12-29T17:25:55.207Z" }, + { url = "https://files.pythonhosted.org/packages/00/0d/3db362169d80442adda9dd563c4f0bb10091c8c1c9a158037f4ecd53988e/backports_zstd-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c", size = 362056, upload-time = "2025-12-29T17:25:56.729Z" }, + { url = "https://files.pythonhosted.org/packages/bd/00/b67ba053a7d6f6dbe2f8a704b7d3a5e01b1d2e2e8edbc9b634f2702ef73c/backports_zstd-1.3.0-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957", size = 505957, upload-time = "2025-12-29T17:25:57.941Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3e/2667c0ddb53ddf28667e330bf9fe92e8e17705a481c9b698e283120565f7/backports_zstd-1.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d", size = 475569, upload-time = "2025-12-29T17:25:59.075Z" }, + { url = "https://files.pythonhosted.org/packages/eb/86/4052473217bd954ccdffda5f7264a0e99e7c4ecf70c0f729845c6a45fc5a/backports_zstd-1.3.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2", size = 581196, upload-time = "2025-12-29T17:26:00.508Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bd/064f6fdb61db3d2c473159ebc844243e650dc032de0f8208443a00127925/backports_zstd-1.3.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f", size = 640888, upload-time = "2025-12-29T17:26:02.134Z" }, + { url = "https://files.pythonhosted.org/packages/d8/09/0822403f40932a165a4f1df289d41653683019e4fd7a86b63ed20e9b6177/backports_zstd-1.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b", size = 491100, upload-time = "2025-12-29T17:26:03.418Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a3/f5ac28d74039b7e182a780809dc66b9dbfc893186f5d5444340bba135389/backports_zstd-1.3.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3", size = 565071, upload-time = "2025-12-29T17:26:05.022Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ac/50209aeb92257a642ee987afa1e61d5b6731ab6bf0bff70905856e5aede6/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1", size = 481519, upload-time = "2025-12-29T17:26:06.255Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/b06f64199fb4b2e9437cedbf96d0155ca08aeec35fe81d41065acd44762e/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5", size = 509465, upload-time = "2025-12-29T17:26:07.602Z" }, + { url = "https://files.pythonhosted.org/packages/f4/37/2c365196e61c8fffbbc930ffd69f1ada7aa1c7210857b3e565031c787ac6/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72", size = 585552, upload-time = "2025-12-29T17:26:08.911Z" }, + { url = "https://files.pythonhosted.org/packages/93/8d/c2c4f448bb6b6c9df17410eaedce415e8db0eb25b60d09a3d22a98294d09/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533", size = 562893, upload-time = "2025-12-29T17:26:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/74/e8/2110d4d39115130f7514cbbcec673a885f4052bb68d15e41bc96a7558856/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c", size = 631462, upload-time = "2025-12-29T17:26:11.99Z" }, + { url = "https://files.pythonhosted.org/packages/b9/a8/d64b59ae0714fdace14e43873f794eff93613e35e3e85eead33a4f44cd80/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f", size = 495125, upload-time = "2025-12-29T17:26:13.218Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/bcff0a091fcf27172c57ae463e49d8dec6dc31e01d7e7bf1ae3aad9c3566/backports_zstd-1.3.0-cp311-cp311-win32.whl", hash = "sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e", size = 288664, upload-time = "2025-12-29T17:26:14.791Z" }, + { url = "https://files.pythonhosted.org/packages/28/1a/379061e2abf8c3150ad51c1baab9ac723e01cf7538860a6a74c48f8b73ee/backports_zstd-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4", size = 313633, upload-time = "2025-12-29T17:26:16.31Z" }, + { url = "https://files.pythonhosted.org/packages/35/e7/eca40858883029fc716660106069b23253e2ec5fd34e86b4101c8cfe864b/backports_zstd-1.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120", size = 288814, upload-time = "2025-12-29T17:26:17.571Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d9/8c9c246e5ea79a4f45d551088b11b61f2dc7efcdc5dbe6df3be84a506e0c/backports_zstd-1.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3", size = 409666, upload-time = "2025-12-29T17:27:57.37Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4f/a55b33c314ca8c9074e99daab54d04c5d212070ae7dbc435329baf1b139e/backports_zstd-1.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a", size = 339199, upload-time = "2025-12-29T17:27:58.542Z" }, + { url = "https://files.pythonhosted.org/packages/9d/13/ce31bd048b1c88d0f65d7af60b6cf89cfbed826c7c978f0ebca9a8a71cfc/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943", size = 420332, upload-time = "2025-12-29T17:28:00.332Z" }, + { url = "https://files.pythonhosted.org/packages/cf/80/c0cdbc533d0037b57248588403a3afb050b2a83b8c38aa608e31b3a4d600/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9", size = 393879, upload-time = "2025-12-29T17:28:01.57Z" }, + { url = "https://files.pythonhosted.org/packages/0f/38/c97428867cac058ed196ccaeddfdf82ecd43b8a65965f2950a6e7547e77a/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5", size = 413842, upload-time = "2025-12-29T17:28:03.113Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ec/6247be6536668fe1c7dfae3eaa9c94b00b956b716957c0fc986ba78c3cc4/backports_zstd-1.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a", size = 299684, upload-time = "2025-12-29T17:28:04.856Z" }, ] [[package]] @@ -121,26 +148,20 @@ wheels = [ [[package]] name = "brotli" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload-time = "2023-09-07T14:03:37.779Z" }, - { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload-time = "2023-09-07T14:03:39.223Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload-time = "2023-09-07T14:03:40.858Z" }, - { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload-time = "2023-09-07T14:03:42.896Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload-time = "2023-09-07T14:03:44.552Z" }, - { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload-time = "2023-09-07T14:03:46.594Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload-time = "2023-09-07T14:03:48.204Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload-time = "2023-09-07T14:03:50.348Z" }, - { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload-time = "2023-09-07T14:03:52.395Z" }, - { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload-time = "2023-09-07T14:03:53.96Z" }, - { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload-time = "2024-10-18T12:32:16.688Z" }, - { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload-time = "2024-10-18T12:32:18.459Z" }, - { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload-time = "2024-10-18T12:32:20.192Z" }, - { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload-time = "2024-10-18T12:32:21.774Z" }, - { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload-time = "2023-09-07T14:03:55.404Z" }, - { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload-time = "2023-09-07T14:03:56.643Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ef/f285668811a9e1ddb47a18cb0b437d5fc2760d537a2fe8a57875ad6f8448/brotli-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744", size = 863110, upload-time = "2025-11-05T18:38:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/50/62/a3b77593587010c789a9d6eaa527c79e0848b7b860402cc64bc0bc28a86c/brotli-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f", size = 445438, upload-time = "2025-11-05T18:38:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e1/7fadd47f40ce5549dc44493877db40292277db373da5053aff181656e16e/brotli-1.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd", size = 1534420, upload-time = "2025-11-05T18:38:15.111Z" }, + { url = "https://files.pythonhosted.org/packages/12/8b/1ed2f64054a5a008a4ccd2f271dbba7a5fb1a3067a99f5ceadedd4c1d5a7/brotli-1.2.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe", size = 1632619, upload-time = "2025-11-05T18:38:16.094Z" }, + { url = "https://files.pythonhosted.org/packages/89/5a/7071a621eb2d052d64efd5da2ef55ecdac7c3b0c6e4f9d519e9c66d987ef/brotli-1.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a", size = 1426014, upload-time = "2025-11-05T18:38:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/26/6d/0971a8ea435af5156acaaccec1a505f981c9c80227633851f2810abd252a/brotli-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b", size = 1489661, upload-time = "2025-11-05T18:38:18.41Z" }, + { url = "https://files.pythonhosted.org/packages/f3/75/c1baca8b4ec6c96a03ef8230fab2a785e35297632f402ebb1e78a1e39116/brotli-1.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3", size = 1599150, upload-time = "2025-11-05T18:38:19.792Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1a/23fcfee1c324fd48a63d7ebf4bac3a4115bdb1b00e600f80f727d850b1ae/brotli-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae", size = 1493505, upload-time = "2025-11-05T18:38:20.913Z" }, + { url = "https://files.pythonhosted.org/packages/36/e5/12904bbd36afeef53d45a84881a4810ae8810ad7e328a971ebbfd760a0b3/brotli-1.2.0-cp311-cp311-win32.whl", hash = "sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03", size = 334451, upload-time = "2025-11-05T18:38:21.94Z" }, + { url = "https://files.pythonhosted.org/packages/02/8b/ecb5761b989629a4758c394b9301607a5880de61ee2ee5fe104b87149ebc/brotli-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24", size = 369035, upload-time = "2025-11-05T18:38:22.941Z" }, ] [[package]] @@ -148,7 +169,7 @@ name = "brotlicffi" version = "1.2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, + { name = "cffi" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/017dc5f852ed9b8735af77774509271acbf1de02d238377667145fcee01d/brotlicffi-1.2.0.1.tar.gz", hash = "sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c", size = 478156, upload-time = "2026-03-05T19:54:11.547Z" } wheels = [ @@ -174,11 +195,11 @@ wheels = [ [[package]] name = "certifi" -version = "2024.7.4" +version = "2026.2.25" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065, upload-time = "2024-07-04T01:36:11.653Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960, upload-time = "2024-07-04T01:36:09.038Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, ] [[package]] @@ -207,38 +228,39 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647, upload-time = "2023-11-01T04:02:55.329Z" }, - { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434, upload-time = "2023-11-01T04:02:57.173Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979, upload-time = "2023-11-01T04:02:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload-time = "2023-11-01T04:02:59.776Z" }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload-time = "2023-11-01T04:03:02.186Z" }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload-time = "2023-11-01T04:03:04.255Z" }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload-time = "2023-11-01T04:03:05.983Z" }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload-time = "2023-11-01T04:03:07.567Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload-time = "2023-11-01T04:03:08.886Z" }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload-time = "2023-11-01T04:03:10.613Z" }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload-time = "2023-11-01T04:03:11.973Z" }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload-time = "2023-11-01T04:03:13.505Z" }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload-time = "2023-11-01T04:03:17.362Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509, upload-time = "2023-11-01T04:03:21.453Z" }, - { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870, upload-time = "2023-11-01T04:03:22.723Z" }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" }, +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] [[package]] name = "click" -version = "8.1.7" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] @@ -281,56 +303,47 @@ toml = [ [[package]] name = "cryptography" -version = "46.0.5" +version = "46.0.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, - { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, - { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, - { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, -] - -[[package]] -name = "decorator" -version = "5.0.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5", size = 34544, upload-time = "2021-05-16T04:08:35.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/36/b1b9bfdf28690ae01d9ca0aa5b0d07cb4448ac65fb91dc7e2d094e3d992f/decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323", size = 8901, upload-time = "2021-05-16T04:08:33.379Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, + { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, + { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, + { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, ] [[package]] @@ -344,36 +357,23 @@ wheels = [ [[package]] name = "ecdsa" -version = "0.19.0" +version = "0.19.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/d0/ec8ac1de7accdcf18cfe468653ef00afd2f609faf67c423efbd02491051b/ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8", size = 197791, upload-time = "2024-04-08T19:01:03.247Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/8de7744cb3bc966c85430ca2d0fcaeea872507c6a4cf6e007f7fe269ed9d/ecdsa-0.19.2.tar.gz", hash = "sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930", size = 202432, upload-time = "2026-03-26T09:58:17.675Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/e7/ed3243b30d1bec41675b6394a1daae46349dc2b855cb83be846a5a918238/ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a", size = 149266, upload-time = "2024-04-08T19:01:00.977Z" }, + { url = "https://files.pythonhosted.org/packages/51/79/119091c98e2bf49e24ed9f3ae69f816d715d2904aefa6a2baa039a2ba0b0/ecdsa-0.19.2-py2.py3-none-any.whl", hash = "sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399", size = 150818, upload-time = "2026-03-26T09:58:15.808Z" }, ] [[package]] name = "filelock" -version = "3.0.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/ec/6ee2168387ce0154632f856d5cc5592328e9cf93127c5c9aeca92c8c16cb/filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", size = 8549, upload-time = "2019-05-18T18:07:03.021Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/83/71a2ee6158bb9f39a90c0dea1637f81d5eef866e188e1971a1b1ab01a35a/filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836", size = 7576, upload-time = "2019-05-18T18:07:01.303Z" }, -] - -[[package]] -name = "flake8-import-order" -version = "0.19.2" +version = "3.25.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycodestyle" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2f/5d2095e2f63b5fabe2d4a7e97c28bfcf901dcd9335650d9e582283bb02b5/flake8_import_order-0.19.2.tar.gz", hash = "sha256:133b3c55497631e4235074fc98a95078bba817832379f22a31f0ad2455bcb0b2", size = 31867, upload-time = "2025-06-24T12:47:39.458Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/d4/3d067f1c4a429e82ec9ae54a346ef50e4d317c6cdfba6bd1443c162ff39f/flake8_import_order-0.19.2-py3-none-any.whl", hash = "sha256:2dfe60175e7195cf36d4c573861fd2e3258cd6650cbd7616da3c6b8193b29b7c", size = 16323, upload-time = "2025-06-24T12:47:38.259Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, ] [[package]] @@ -424,47 +424,45 @@ wheels = [ [[package]] name = "flask-compress" -version = "1.17" +version = "1.23" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-zstd" }, { name = "brotli", marker = "platform_python_implementation != 'PyPy'" }, { name = "brotlicffi", marker = "platform_python_implementation == 'PyPy'" }, { name = "flask" }, - { name = "zstandard" }, - { name = "zstandard", marker = "platform_python_implementation == 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/1f/260db5a4517d59bfde7b4a0d71052df68fb84983bda9231100e3b80f5989/flask_compress-1.17.tar.gz", hash = "sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8", size = 15733, upload-time = "2024-10-14T08:13:33.196Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/e4/2b54da5cf8ae5d38a495ca20154aa40d6d2ee6dc1756429a82856181aa2c/flask_compress-1.23.tar.gz", hash = "sha256:5580935b422e3f136b9a90909e4b1015ac2b29c9aebe0f8733b790fde461c545", size = 20135, upload-time = "2025-11-06T09:06:29.56Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/54/ff08f947d07c0a8a5d8f1c8e57b142c97748ca912b259db6467ab35983cd/Flask_Compress-1.17-py3-none-any.whl", hash = "sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20", size = 8723, upload-time = "2024-10-14T08:13:31.726Z" }, + { url = "https://files.pythonhosted.org/packages/7d/9a/bebdcdba82d2786b33cd9f5fd65b8d309797c27176a9c4f357c1150c4ac0/flask_compress-1.23-py3-none-any.whl", hash = "sha256:52108afb4d133a5aab9809e6ac3c085ed7b9c788c75c6846c129faa28468f08c", size = 10515, upload-time = "2025-11-06T09:06:28.691Z" }, ] [[package]] name = "flask-cors" -version = "5.0.0" +version = "6.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, + { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/d0/d9e52b154e603b0faccc0b7c2ad36a764d8755ef4036acbf1582a67fb86b/flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef", size = 30954, upload-time = "2024-08-31T00:44:26.395Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/07/1afa0514c876282bebc1c9aee83c6bb98fe6415cf57b88d9b06e7e29bf9c/Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc", size = 14463, upload-time = "2024-08-31T00:44:24.394Z" }, + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, ] [[package]] name = "flask-jwt-oidc" -version = "0.8.0" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachelib" }, { name = "cryptography" }, { name = "flask" }, { name = "pyjwt" }, - { name = "six" }, - { name = "zimports" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/d2/4be4b075b930ab19d537bd8d7cbeeb3492a966b19351c67a9582a6a34438/flask_jwt_oidc-0.8.0.tar.gz", hash = "sha256:fe1c28d3c71a1ec56b09f586f5dcda0357df7be4895656737b6268557c2d15e4", size = 7551, upload-time = "2025-01-27T21:58:44.554Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/59/8827de70afedb42ce19c28aeb2d166e9751538f5b7fa5b6303354ff6fb5d/flask_jwt_oidc-0.9.0.tar.gz", hash = "sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3", size = 7532, upload-time = "2026-03-14T15:53:01.502Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b3/69b3c09824b9db259ef35cdd757ccd16cc4db31fb98307bb5d138a59f7db/flask_jwt_oidc-0.8.0-py3-none-any.whl", hash = "sha256:9be9b9eba9824888ae04bdc8c6af15fa6ce5d2013129c9a1a9990b8412fc63e0", size = 9515, upload-time = "2025-01-27T21:58:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/04/08/85dde23b8855e8beb1a5014632fcf8da20242e99669af9fc4c1432bf1a64/flask_jwt_oidc-0.9.0-py3-none-any.whl", hash = "sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c", size = 9679, upload-time = "2026-03-14T15:53:00.026Z" }, ] [[package]] @@ -606,28 +604,22 @@ wheels = [ [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "idna" -version = "3.7" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", size = 189575, upload-time = "2024-04-11T03:34:43.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", size = 66836, upload-time = "2024-04-11T03:34:41.447Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] -[[package]] -name = "ijson" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/12/3116e1d5752aa9d480eb58ae4b348d38c1aeaf792c5fbca22e44c27d4bf1/ijson-2.6.1.tar.gz", hash = "sha256:75ebc60b23abfb1c97f475ab5d07a5ed725ad4bd1f58513d8b258c21f02703d0", size = 29393, upload-time = "2020-02-03T08:24:19.502Z" } - [[package]] name = "importlib-resources" version = "6.5.2" @@ -678,7 +670,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.22.0" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -686,21 +678,21 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/f1/1c1dc0f6b3bf9e76f7526562d29c320fa7d6a2f35b37a1392cc0acd58263/jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", size = 325490, upload-time = "2024-04-30T19:44:37.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/2f/324fab4be6fe37fb7b521546e8a557e6cf08c1c1b3d0b4839a00f589d9ef/jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802", size = 88316, upload-time = "2024-04-30T19:44:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b9/cc0cc592e7c195fb8a650c1d5990b10175cf13b4c97465c72ec841de9e4b/jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", size = 13983, upload-time = "2023-12-25T15:16:53.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/07/44bd408781594c4d0a027666ef27fab1e441b109dc3b76b4f836f8fd04fe/jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c", size = 18482, upload-time = "2023-12-25T15:16:51.997Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] @@ -718,59 +710,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/0f/834427d8c03ff1d7e867d3db3d176470c64871753252b21b4f4897d1fa45/kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93", size = 214219, upload-time = "2025-12-29T20:30:05.74Z" }, ] -[[package]] -name = "lazy-object-proxy" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/f0/f02e2d150d581a294efded4020094a371bbab42423fe78625ac18854d89b/lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69", size = 43271, upload-time = "2023-12-15T15:11:41.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/e1/99a7ec68b892c9b8c6212617f54e7e9b0304d47edad8c0ff043ae3aeb1a9/lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c", size = 27434, upload-time = "2023-12-15T15:10:56.157Z" }, - { url = "https://files.pythonhosted.org/packages/1a/76/6a41de4b44d1dcfe4c720d4606de0d7b69b6b450f0bdce16f2e1fb8abc89/lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4", size = 70687, upload-time = "2023-12-15T15:10:57.949Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5d/eaa12126e8989c9bdd21d864cbba2b258cb9ee2f574ada1462a0004cfad8/lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56", size = 69757, upload-time = "2023-12-15T15:10:59.937Z" }, - { url = "https://files.pythonhosted.org/packages/53/a9/6f22cfe9572929656988b72c0de266c5d10755369b575322725f67364c4e/lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9", size = 73709, upload-time = "2023-12-15T15:11:02.161Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e6/b10fd94710a99a6309f3ad61a4eb480944bbb17fcb41bd2d852fdbee57ee/lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f", size = 73191, upload-time = "2023-12-15T15:11:03.511Z" }, - { url = "https://files.pythonhosted.org/packages/c9/78/a9b9d314da02fe66b632f2354e20e40fc3508befb450b5a17987a222b383/lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03", size = 25773, upload-time = "2023-12-15T15:11:04.781Z" }, - { url = "https://files.pythonhosted.org/packages/94/e6/e2d3b0c9efe61f72dc327ce2355941f540e0b0d1f2b3490cbab6bab7d3ea/lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6", size = 27550, upload-time = "2023-12-15T15:11:05.915Z" }, - { url = "https://files.pythonhosted.org/packages/31/8b/94dc8d58704ab87b39faed6f2fc0090b9d90e2e2aa2bbec35c79f3d2a054/lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d", size = 16405, upload-time = "2023-12-15T15:11:40.453Z" }, -] - [[package]] name = "mako" -version = "1.3.3" +version = "1.3.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/dc/48e8853daf4b32748d062ce9cd47a744755fb60691ebc211ca689b849c1c/Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73", size = 389980, upload-time = "2024-04-10T15:32:42.69Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/c9/9cd84cbd5816aa8bee5fd5a00f857efd636ec30586848d571b67baf0b868/Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40", size = 78829, upload-time = "2024-04-10T15:32:47.482Z" }, + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, ] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384, upload-time = "2024-02-02T16:31:22.863Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219, upload-time = "2024-02-02T16:30:19.988Z" }, - { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098, upload-time = "2024-02-02T16:30:21.063Z" }, - { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014, upload-time = "2024-02-02T16:30:22.926Z" }, - { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220, upload-time = "2024-02-02T16:30:24.76Z" }, - { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756, upload-time = "2024-02-02T16:30:25.877Z" }, - { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988, upload-time = "2024-02-02T16:30:26.935Z" }, - { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718, upload-time = "2024-02-02T16:30:28.111Z" }, - { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317, upload-time = "2024-02-02T16:30:29.214Z" }, - { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670, upload-time = "2024-02-02T16:30:30.915Z" }, - { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224, upload-time = "2024-02-02T16:30:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, ] [[package]] name = "marshmallow" -version = "4.2.2" +version = "4.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/03/261af5efb3d3ce0e2db3fd1e11dc5a96b74a4fb76e488da1c845a8f12345/marshmallow-4.2.2.tar.gz", hash = "sha256:ba40340683a2d1c15103647994ff2f6bc2c8c80da01904cbe5d96ee4baa78d9f", size = 221404, upload-time = "2026-02-04T15:47:03.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/ed/ecdd3a8259680dc9626ed5971b110c9be560962e11f7b0319b45ec2092b1/marshmallow-4.2.3.tar.gz", hash = "sha256:3e3fef6b3603721a25a723b8caedfa488369bddaf9bc03b40b9442c90aebd22b", size = 222932, upload-time = "2026-03-25T23:27:18.237Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/70/bb89f807a6a6704bdc4d6f850d5d32954f6c1965e3248e31455defdf2f30/marshmallow-4.2.2-py3-none-any.whl", hash = "sha256:084a9466111b7ec7183ca3a65aed758739af919fedc5ebdab60fb39d6b4dc121", size = 48454, upload-time = "2026-02-04T15:47:02.013Z" }, + { url = "https://files.pythonhosted.org/packages/58/2e/ce3278954d44903ccf8606c2a16e6b3ad8e10146c21de1b7b9b0f5812b23/marshmallow-4.2.3-py3-none-any.whl", hash = "sha256:c84fd89817ecea690bde1eb925036070e5e9883148217585adc56d5cfbc082b8", size = 48876, upload-time = "2026-03-25T23:27:16.907Z" }, ] [[package]] @@ -788,11 +765,11 @@ wheels = [ [[package]] name = "mccabe" -version = "0.6.1" +version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/18/fa675aa501e11d6d6ca0ae73a101b2f3571a565e0f7d38e062eec18a91ee/mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f", size = 8612, upload-time = "2017-01-26T22:13:15.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/89/479dc97e18549e21354893e4ee4ef36db1d237534982482c3681ee6e7b57/mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", size = 8556, upload-time = "2017-01-26T22:13:14.36Z" }, + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, ] [[package]] @@ -811,22 +788,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl", hash = "sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e", size = 93751, upload-time = "2025-11-27T00:37:13.993Z" }, ] -[[package]] -name = "oauthlib" -version = "3.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352, upload-time = "2022-10-17T20:04:27.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" }, -] - [[package]] name = "packaging" -version = "24.0" +version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", size = 147882, upload-time = "2024-03-10T09:39:28.33Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", size = 53488, upload-time = "2024-03-10T09:39:25.947Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] @@ -868,20 +836,11 @@ wheels = [ [[package]] name = "pyasn1" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/a3/d2157f333900747f20984553aca98008b6dc843eb62f3a36030140ccec0d/pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", size = 148088, upload-time = "2024-03-26T20:07:35.376Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/7e/5f50d07d5e70a2addbccd90ac2950f81d1edd0783630651d9268d7f1db49/pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473", size = 85313, upload-time = "2024-03-26T20:07:32.491Z" }, -] - -[[package]] -name = "pycodestyle" -version = "2.14.0" +version = "0.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, ] [[package]] @@ -912,15 +871,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, ] -[[package]] -name = "pyflakes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -957,15 +907,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, ] -[[package]] -name = "pyparsing" -version = "3.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/60/9bed18f43275b34198eb9720d4c1238c68b3755620d20df0afd89424d32b/pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea", size = 884709, upload-time = "2022-01-21T05:41:34.625Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/c1/23fd82ad3121656b585351aba6c19761926bb0db2ebed9e4ff09a43a3fcc/pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484", size = 98049, upload-time = "2022-01-21T05:41:33.032Z" }, -] - [[package]] name = "pytest" version = "9.0.2" @@ -984,16 +925,16 @@ wheels = [ [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] @@ -1031,25 +972,16 @@ wheels = [ [[package]] name = "python-jose" -version = "3.3.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ecdsa" }, { name = "pyasn1" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/19/b2c86504116dc5f0635d29f802da858404d77d930a25633d2e86a64a35b3/python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a", size = 129068, upload-time = "2021-06-05T03:30:40.895Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/2d/e94b2f7bab6773c70efc70a61d66e312e1febccd9e0db6b9e0adf58cbad1/python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a", size = 33530, upload-time = "2021-06-05T03:30:38.099Z" }, -] - -[[package]] -name = "python-magic" -version = "0.4.27" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, ] [[package]] @@ -1072,22 +1004,7 @@ source = { editable = "." } dependencies = [ { name = "alembic" }, { name = "amqp" }, - { name = "aniso8601" }, - { name = "argon2-cffi" }, - { name = "argon2-cffi-bindings" }, - { name = "attrs" }, - { name = "bidict" }, - { name = "blinker" }, - { name = "brotli" }, - { name = "cachelib" }, - { name = "certifi" }, - { name = "cffi" }, - { name = "charset-normalizer" }, { name = "click" }, - { name = "cryptography" }, - { name = "decorator" }, - { name = "dill" }, - { name = "ecdsa" }, { name = "filelock" }, { name = "flask" }, { name = "flask-admin" }, @@ -1102,69 +1019,27 @@ dependencies = [ { name = "flask-socketio" }, { name = "flask-sqlalchemy" }, { name = "gevent" }, - { name = "greenlet" }, { name = "gunicorn" }, - { name = "h11" }, - { name = "idna" }, - { name = "ijson" }, - { name = "itsdangerous" }, { name = "jinja2" }, - { name = "jsonschema" }, - { name = "jsonschema-specifications" }, { name = "kombu" }, - { name = "lazy-object-proxy" }, - { name = "mako" }, { name = "markupsafe" }, { name = "marshmallow" }, { name = "marshmallow-sqlalchemy" }, { name = "minio" }, - { name = "oauthlib" }, - { name = "packaging" }, - { name = "platformdirs" }, { name = "psycopg2-binary" }, - { name = "pyasn1" }, - { name = "pycparser" }, - { name = "pycryptodome" }, - { name = "pygments" }, - { name = "pyjwt" }, - { name = "pyparsing" }, { name = "python-dateutil" }, { name = "python-dotenv" }, - { name = "python-engineio" }, { name = "python-jose" }, - { name = "python-magic" }, - { name = "python-socketio" }, { name = "redis" }, - { name = "referencing" }, { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "rpds-py" }, - { name = "rsa" }, - { name = "simple-websocket" }, { name = "snowplow-tracker" }, { name = "sqlalchemy" }, - { name = "sqlalchemy-utc" }, - { name = "sqlalchemy-utils" }, - { name = "toml" }, - { name = "typing-extensions" }, - { name = "tzdata" }, - { name = "urllib3" }, - { name = "vine" }, - { name = "werkzeug" }, - { name = "wrapt" }, - { name = "wsproto" }, { name = "wtforms" }, - { name = "zope-event" }, - { name = "zope-interface" }, - { name = "zstandard" }, ] [package.dev-dependencies] dev = [ - { name = "astroid" }, { name = "isort" }, - { name = "mccabe" }, - { name = "pluggy" }, { name = "pylint" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -1172,104 +1047,47 @@ dev = [ [package.metadata] requires-dist = [ - { name = "alembic", specifier = "==1.18.4" }, - { name = "amqp", specifier = "==5.2.0" }, - { name = "aniso8601", specifier = "==9.0.1" }, - { name = "argon2-cffi", specifier = "==25.1.0" }, - { name = "argon2-cffi-bindings", specifier = "==25.1.0" }, - { name = "attrs", specifier = "==23.2.0" }, - { name = "bidict", specifier = "==0.23.1" }, - { name = "blinker", specifier = "==1.9.0" }, - { name = "brotli", specifier = "==1.1.0" }, - { name = "cachelib", specifier = "==0.13.0" }, - { name = "certifi", specifier = "==2024.7.4" }, - { name = "cffi", specifier = "==2.0.0" }, - { name = "charset-normalizer", specifier = "==3.3.2" }, - { name = "click", specifier = "==8.1.7" }, - { name = "cryptography", specifier = "==46.0.5" }, - { name = "decorator", specifier = "==5.0.9" }, - { name = "dill", specifier = "==0.4.1" }, - { name = "ecdsa", specifier = "==0.19.0" }, - { name = "filelock", specifier = "==3.0.12" }, - { name = "flask", specifier = "==3.1.3" }, - { name = "flask-admin", specifier = "==2.0.2" }, - { name = "flask-caching", specifier = "==2.3.1" }, - { name = "flask-compress", specifier = "==1.17" }, - { name = "flask-cors", specifier = "==5.0.0" }, - { name = "flask-jwt-oidc", specifier = "==0.8.0" }, - { name = "flask-login", specifier = "==0.6.3" }, - { name = "flask-marshmallow", specifier = "==1.4.0" }, - { name = "flask-migrate", specifier = "==4.1.0" }, - { name = "flask-restx", specifier = "==1.3.2" }, - { name = "flask-socketio", specifier = "==5.6.1" }, - { name = "flask-sqlalchemy", specifier = "==3.1.1" }, - { name = "gevent", specifier = "==25.9.1" }, - { name = "greenlet", specifier = "==3.3.2" }, - { name = "gunicorn", specifier = "==25.2.0" }, - { name = "h11", specifier = "==0.14.0" }, - { name = "idna", specifier = "==3.7" }, - { name = "ijson", specifier = "==2.6.1" }, - { name = "itsdangerous", specifier = "==2.2.0" }, - { name = "jinja2", specifier = "==3.1.6" }, - { name = "jsonschema", specifier = "==4.22.0" }, - { name = "jsonschema-specifications", specifier = "==2023.12.1" }, - { name = "kombu", specifier = "==5.6.2" }, - { name = "lazy-object-proxy", specifier = "==1.10.0" }, - { name = "mako", specifier = "==1.3.3" }, - { name = "markupsafe", specifier = "==2.1.5" }, - { name = "marshmallow", specifier = "==4.2.2" }, - { name = "marshmallow-sqlalchemy", specifier = "==1.4.2" }, - { name = "minio", specifier = "==7.2.20" }, - { name = "oauthlib", specifier = "==3.2.2" }, - { name = "packaging", specifier = "==24.0" }, - { name = "platformdirs", specifier = "==4.9.4" }, - { name = "psycopg2-binary", specifier = "==2.9.11" }, - { name = "pyasn1", specifier = "==0.6.0" }, - { name = "pycparser", specifier = "==3.0" }, - { name = "pycryptodome", specifier = "==3.23.0" }, - { name = "pygments", specifier = "==2.19.2" }, - { name = "pyjwt", specifier = "==2.12.1" }, - { name = "pyparsing", specifier = "==3.0.7" }, - { name = "python-dateutil", specifier = "==2.9.0.post0" }, - { name = "python-dotenv", specifier = "==1.2.2" }, - { name = "python-engineio", specifier = "==4.13.1" }, - { name = "python-jose", specifier = "==3.3.0" }, - { name = "python-magic", specifier = "==0.4.27" }, - { name = "python-socketio", specifier = "==5.16.1" }, - { name = "redis", specifier = "==7.4.0" }, - { name = "referencing", specifier = "==0.35.1" }, - { name = "requests", specifier = "==2.32.5" }, - { name = "requests-oauthlib", specifier = "==1.3.1" }, - { name = "rpds-py", specifier = "==0.18.1" }, - { name = "rsa", specifier = "==4.9.1" }, - { name = "simple-websocket", specifier = "==1.0.0" }, - { name = "snowplow-tracker", specifier = "==1.1.0" }, - { name = "sqlalchemy", specifier = "==2.0.48" }, - { name = "sqlalchemy-utc", specifier = "==0.14.0" }, - { name = "sqlalchemy-utils", specifier = "==0.42.1" }, - { name = "toml", specifier = "==0.10.2" }, - { name = "typing-extensions", specifier = "==4.15.0" }, - { name = "tzdata", specifier = "==2025.3" }, - { name = "urllib3", specifier = "==2.6.3" }, - { name = "vine", specifier = "==5.1.0" }, - { name = "werkzeug", specifier = "==3.1.6" }, - { name = "wrapt", specifier = "==1.12.1" }, - { name = "wsproto", specifier = "==1.2.0" }, - { name = "wtforms", specifier = "==3.2.1" }, - { name = "zope-event", specifier = "==5.0" }, - { name = "zope-interface", specifier = "==6.3" }, - { name = "zstandard", specifier = "==0.25.0" }, + { name = "alembic", specifier = ">=1.18,<1.19" }, + { name = "amqp", specifier = ">=5.3,<5.4" }, + { name = "click", specifier = ">=8.3,<8.4" }, + { name = "filelock", specifier = ">=3.25,<3.26" }, + { name = "flask", specifier = ">=3.1,<3.2" }, + { name = "flask-admin", specifier = ">=2.0,<2.1" }, + { name = "flask-caching", specifier = ">=2.3,<2.4" }, + { name = "flask-compress", specifier = ">=1.23,<1.24" }, + { name = "flask-cors", specifier = ">=6.0,<6.1" }, + { name = "flask-jwt-oidc", specifier = ">=0.9,<0.10" }, + { name = "flask-login", specifier = ">=0.6,<0.7" }, + { name = "flask-marshmallow", specifier = ">=1.4,<1.5" }, + { name = "flask-migrate", specifier = ">=4.1,<4.2" }, + { name = "flask-restx", specifier = ">=1.3,<1.4" }, + { name = "flask-socketio", specifier = ">=5.6,<5.7" }, + { name = "flask-sqlalchemy", specifier = ">=3.1,<3.2" }, + { name = "gevent", specifier = ">=25.9,<25.10" }, + { name = "gunicorn", specifier = ">=25.2,<25.3" }, + { name = "jinja2", specifier = ">=3.1,<3.2" }, + { name = "kombu", specifier = ">=5.6,<5.7" }, + { name = "markupsafe", specifier = ">=3.0,<3.1" }, + { name = "marshmallow", specifier = ">=4.2,<4.3" }, + { name = "marshmallow-sqlalchemy", specifier = ">=1.4,<1.5" }, + { name = "minio", specifier = ">=7.2,<7.3" }, + { name = "psycopg2-binary", specifier = ">=2.9,<2.10" }, + { name = "python-dateutil", specifier = ">=2.9,<2.10" }, + { name = "python-dotenv", specifier = ">=1.2,<1.3" }, + { name = "python-jose", specifier = ">=3.5,<3.6" }, + { name = "redis", specifier = ">=7.4,<7.5" }, + { name = "requests", specifier = ">=2.33,<2.34" }, + { name = "snowplow-tracker", specifier = ">=1.1,<1.2" }, + { name = "sqlalchemy", specifier = ">=2.0,<2.1" }, + { name = "wtforms", specifier = ">=3.2,<3.3" }, ] [package.metadata.requires-dev] dev = [ - { name = "astroid", specifier = "==4.0.4" }, - { name = "isort", specifier = "==8.0.1" }, - { name = "mccabe", specifier = "==0.6.1" }, - { name = "pluggy", specifier = "==1.6.0" }, - { name = "pylint", specifier = "==4.0.5" }, - { name = "pytest", specifier = "==9.0.2" }, - { name = "pytest-cov", specifier = "==7.0.0" }, + { name = "isort", specifier = ">=8.0,<8.1" }, + { name = "pylint", specifier = ">=4.0,<4.1" }, + { name = "pytest", specifier = ">=9.0,<9.1" }, + { name = "pytest-cov", specifier = ">=7.1,<7.2" }, ] [[package]] @@ -1286,20 +1104,21 @@ wheels = [ [[package]] name = "referencing" -version = "0.35.1" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991, upload-time = "2024-05-01T20:26:04.574Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684, upload-time = "2024-05-01T20:26:02.078Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1307,43 +1126,44 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "requests-oauthlib" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/52/531ef197b426646f26b53815a7d2a67cb7a331ef098bb276db26a68ac49f/requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a", size = 52027, upload-time = "2022-01-29T18:52:24.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/bb/5deac77a9af870143c684ab46a7934038a53eb4aa975bc0687ed6ca2c610/requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", size = 23892, upload-time = "2022-01-29T18:52:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] name = "rpds-py" -version = "0.18.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/aa/e7c404bdee1db7be09860dff423d022ffdce9269ec8e6532cce09ee7beea/rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", size = 25388, upload-time = "2024-05-06T13:28:34.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/f3/454ef9c66219ea511991e024f3a379fca618acd4cbe12e369b2d02f9d0b6/rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", size = 327805, upload-time = "2024-05-06T13:25:02.669Z" }, - { url = "https://files.pythonhosted.org/packages/58/e3/b5eb611e2d51688726533bb97b420d36a55d4560c53d016e977ff6d48116/rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", size = 322329, upload-time = "2024-05-06T13:25:05.666Z" }, - { url = "https://files.pythonhosted.org/packages/92/48/32bed868dd4e7d16e26457cc0b8634d6b16cb0e082a93f044ef5f7c77361/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", size = 1114289, upload-time = "2024-05-06T13:25:08.806Z" }, - { url = "https://files.pythonhosted.org/packages/77/66/905aa687ea072d8980f7b14eb9e3c3023f5f3892eb8b88be024094956014/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", size = 1123667, upload-time = "2024-05-06T13:25:11.624Z" }, - { url = "https://files.pythonhosted.org/packages/4e/6c/c658183fc2d2a6ed97b0816ab4fef59d609e596bf6f5f0898ae955c14885/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", size = 1145398, upload-time = "2024-05-06T13:25:14.607Z" }, - { url = "https://files.pythonhosted.org/packages/91/33/b680feac0159b5b66ebb9e6d635d78e94486b0b7ed58bea273be2cc80817/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", size = 1309726, upload-time = "2024-05-06T13:25:16.757Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a0/a3702128743ae5bf14175a7333a4741db167f62d42f70e0edc15d9cd45c5/rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", size = 1114522, upload-time = "2024-05-06T13:25:19.815Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/a4ed8b067a8f55df92dc1bc4d20858f02ddfdba3057f96759f4dd1bff7af/rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", size = 1139472, upload-time = "2024-05-06T13:25:22.14Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ba/adb81247a2fe211ff74cd4c2790454bbf37eb7430ee898e4aa7ce5cb6507/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", size = 1277148, upload-time = "2024-05-06T13:25:25.122Z" }, - { url = "https://files.pythonhosted.org/packages/52/fc/1eb8dcf82ec8d1252c060644fa44478660e94637ddd91dc8d452b467f359/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", size = 1304556, upload-time = "2024-05-06T13:25:27.737Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3f/0b8e2ac89076fede032aae3fc9cf9390c6fd99a470b238fb62bbc64f4cbf/rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", size = 1283410, upload-time = "2024-05-06T13:25:29.843Z" }, - { url = "https://files.pythonhosted.org/packages/74/e0/f9dc97509b3048ddc3ab7b0cd48bd25f78dff45bec463c62b0171c57398c/rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb", size = 196622, upload-time = "2024-05-06T13:25:31.794Z" }, - { url = "https://files.pythonhosted.org/packages/ff/26/0778cc18815f20e37eb492bfed622d01722db38b2f3f86790753b01b2a73/rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2", size = 209016, upload-time = "2024-05-06T13:25:33.646Z" }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] [[package]] @@ -1358,25 +1178,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] -[[package]] -name = "setuptools" -version = "82.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, -] - [[package]] name = "simple-websocket" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wsproto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/82/3cf87d317911864a2f2a8daf1779fc7f82d5d55e6a8aaa0315f8209047a7/simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8", size = 13071, upload-time = "2023-10-05T16:28:30.143Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/d4/bfa032f961103eba93de583b161f0e6a5b63cebb8f2c7d0c6e6efe1e3d2e/simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4", size = 17300, upload-time = "2024-10-10T22:39:31.412Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/ea/288a8ac1d9551354488ff60c0ac6a76acc3b6b60f0460ac1944c75e240da/simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc", size = 13712, upload-time = "2023-10-05T16:28:28.761Z" }, + { url = "https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c", size = 13842, upload-time = "2024-10-10T22:39:29.645Z" }, ] [[package]] @@ -1421,40 +1232,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, ] -[[package]] -name = "sqlalchemy-utc" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, - { name = "sqlalchemy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/db/2d8f421cece2758954278bd16e05e3f73ce86c0062577a1ccfd3eb4208cd/SQLAlchemy-Utc-0.14.0.tar.gz", hash = "sha256:8e041624595b66d7b1d5ea8b6de486df5c1b9352697f3b24f862f0ded56cd7aa", size = 5353, upload-time = "2021-09-24T04:37:58.19Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/89/ccfe4b579784f852c64f0fcb127e560990c2d75a2514dda51a9c87fb3782/SQLAlchemy_Utc-0.14.0-py2.py3-none-any.whl", hash = "sha256:d2379eed5cce372128b5e744ce382decd262b2c742ab31f7f22ca11c6647f60b", size = 5979, upload-time = "2021-09-24T04:37:56.555Z" }, -] - -[[package]] -name = "sqlalchemy-utils" -version = "0.42.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sqlalchemy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/7d/eb9565b6a49426552a5bf5c57e7c239c506dc0e4e5315aec6d1e8241dc7c/sqlalchemy_utils-0.42.1.tar.gz", hash = "sha256:881f9cd9e5044dc8f827bccb0425ce2e55490ce44fc0bb848c55cc8ee44cc02e", size = 130789, upload-time = "2025-12-13T03:14:13.591Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/25/7400c18c3ee97914cc99c90007795c00a4ec5b60c853b49db7ba24d11179/sqlalchemy_utils-0.42.1-py3-none-any.whl", hash = "sha256:243cfe1b3a1dae3c74118ae633f1d1e0ed8c787387bc33e556e37c990594ac80", size = 91761, upload-time = "2025-12-13T03:14:15.014Z" }, -] - -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, -] - [[package]] name = "tomli" version = "2.4.1" @@ -1520,32 +1297,26 @@ wheels = [ [[package]] name = "werkzeug" -version = "3.1.6" +version = "3.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/43/76ded108b296a49f52de6bac5192ca1c4be84e886f9b5c9ba8427d9694fd/werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351", size = 875700, upload-time = "2026-03-24T01:08:07.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b2/0bba9bbb4596d2d2f285a16c2ab04118f6b957d8441566e1abb892e6a6b2/werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f", size = 226295, upload-time = "2026-03-24T01:08:06.133Z" }, ] -[[package]] -name = "wrapt" -version = "1.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/f7/e43cefbe88c5fd371f4cf0cf5eb3feccd07515af9fd6cf7dbf1d1793a797/wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7", size = 27488, upload-time = "2020-03-09T02:32:04.07Z" } - [[package]] name = "wsproto" -version = "1.2.0" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload-time = "2022-08-23T19:58:21.447Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, ] [[package]] @@ -1560,70 +1331,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/c9/2088fb5645cd289c99ebe0d4cdcc723922a1d8e1beaefb0f6f76dff9b21c/wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4", size = 152454, upload-time = "2024-10-21T11:33:58.44Z" }, ] -[[package]] -name = "zimports" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8-import-order" }, - { name = "pyflakes" }, - { name = "tomli" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/36/5ecb5c9bc1eaf743c1526bcca51c4c58a9f7f67440328cd9a6cd2437b01a/zimports-0.6.3.tar.gz", hash = "sha256:0091c43de53f6976be05d5a9ccf9455bc5730f5d6f8a0f9544bc9eb6db0f4bb6", size = 17970, upload-time = "2025-11-04T15:23:37.454Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/9e/1222fda922f7cd7bba2e754998fe1c30e7b19b9b833c61d29a8900ab42de/zimports-0.6.3-py3-none-any.whl", hash = "sha256:33adb19d62a2206c9256082752cd4d3b0695e5e9f9cb8184558573b0992ae3fe", size = 20411, upload-time = "2025-11-04T15:23:36.257Z" }, -] - [[package]] name = "zope-event" -version = "5.0" +version = "6.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350, upload-time = "2023-06-23T06:28:35.709Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/33/d3eeac228fc14de76615612ee208be2d8a5b5b0fada36bf9b62d6b40600c/zope_event-6.1.tar.gz", hash = "sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0", size = 18739, upload-time = "2025-11-07T08:05:49.934Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824, upload-time = "2023-06-23T06:28:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b0/956902e5e1302f8c5d124e219c6bf214e2649f92ad5fce85b05c039a04c9/zope_event-6.1-py3-none-any.whl", hash = "sha256:0ca78b6391b694272b23ec1335c0294cc471065ed10f7f606858fc54566c25a0", size = 6414, upload-time = "2025-11-07T08:05:48.874Z" }, ] [[package]] name = "zope-interface" -version = "6.3" +version = "8.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/bd/a30bf6df24480017171da4f52ee527a72c7a6450c86355011e0156e71723/zope.interface-6.3.tar.gz", hash = "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a", size = 294679, upload-time = "2024-04-12T15:14:21.416Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ff/37b37e408908f6d949cea8b01969204fc76dd0b85eabc41ff8ca3306a940/zope.interface-6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39", size = 202694, upload-time = "2024-04-12T15:14:27.446Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3d/b5562aa226faec1705705bd57fc98e87eb857ab20efa0772734b88bd2fce/zope.interface-6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299", size = 202786, upload-time = "2024-04-12T15:14:29.356Z" }, - { url = "https://files.pythonhosted.org/packages/35/0c/2442f1d7fd3fc2f4179892c161f79e7d3dd5dd483c79a57c3f6355675374/zope.interface-6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130", size = 249832, upload-time = "2024-04-12T15:33:08.616Z" }, - { url = "https://files.pythonhosted.org/packages/55/41/79c9014351824b13db0c37808333816401e87d473d268f137de172bacf5a/zope.interface-6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10", size = 243984, upload-time = "2024-04-12T15:14:04.123Z" }, - { url = "https://files.pythonhosted.org/packages/95/00/1c97f0b1622b4eb587e8aea84f109b426c6d3506fba043052dafeb4cab95/zope.interface-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e", size = 249354, upload-time = "2024-04-12T15:14:43.49Z" }, - { url = "https://files.pythonhosted.org/packages/44/25/f993b704a15e75da08ed8ee8cb3cfdb61eb9ccc5d68e0db887e3961520e9/zope.interface-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061", size = 204423, upload-time = "2024-04-12T15:21:41.029Z" }, -] - -[[package]] -name = "zstandard" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, - { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, - { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, - { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, - { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, - { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, - { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, - { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, - { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, - { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/86/a4/77daa5ba398996d16bb43fc721599d27d03eae68fe3c799de1963c72e228/zope_interface-8.2.tar.gz", hash = "sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224", size = 254019, upload-time = "2026-01-09T07:51:07.253Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/97/9c2aa8caae79915ed64eb114e18816f178984c917aa9adf2a18345e4f2e5/zope_interface-8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322", size = 208081, upload-time = "2026-01-09T08:05:06.623Z" }, + { url = "https://files.pythonhosted.org/packages/34/86/4e2fcb01a8f6780ac84923748e450af0805531f47c0956b83065c99ab543/zope_interface-8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b", size = 208522, upload-time = "2026-01-09T08:05:07.986Z" }, + { url = "https://files.pythonhosted.org/packages/f6/eb/08e277da32ddcd4014922854096cf6dcb7081fad415892c2da1bedefbf02/zope_interface-8.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466", size = 255198, upload-time = "2026-01-09T08:05:09.532Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a1/b32484f3281a5dc83bc713ad61eca52c543735cdf204543172087a074a74/zope_interface-8.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c", size = 259970, upload-time = "2026-01-09T08:05:11.477Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/bca0e8ae1e487d4093a8a7cfed2118aa2d4758c8cfd66e59d2af09d71f1c/zope_interface-8.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce", size = 261153, upload-time = "2026-01-09T08:05:13.402Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/e3ff2a708011e56b10b271b038d4cb650a8ad5b7d24352fe2edf6d6b187a/zope_interface-8.2-cp311-cp311-win_amd64.whl", hash = "sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489", size = 212330, upload-time = "2026-01-09T08:05:15.267Z" }, ] From 3d910a3e8e768cd96b087f141861637f94d7408d Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 10:06:59 -0700 Subject: [PATCH 20/81] Update to 3.14 --- .devcontainer/devcontainer.json | 7 +- .devcontainer/docker-compose.yml | 9 +- api/migrations/versions/c388edd5dc5a_.py | 129 +++++++ api/pyproject.toml | 3 +- api/uv.lock | 470 +++++++++++++---------- 5 files changed, 399 insertions(+), 219 deletions(-) create mode 100644 api/migrations/versions/c388edd5dc5a_.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 96d96af77..fe16a573f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -27,16 +27,19 @@ "dbaeumer.vscode-eslint", "github.vscode-pull-request-github", "ms-python.python", - "ms-python.pylint", "ms-python.vscode-pylance", "ms-python.debugpy", "mtxr.sqltools", "mtxr.sqltools-driver-pg", "octref.vetur", - "sonarsource.sonarlint-vscode" + "sonarsource.sonarlint-vscode", + "charliermarsh.ruff" ], "settings": { + "python-envs.terminal.autoActivationType": "shellStartup", + "python.terminal.activateEnvironment": false, "python.languageServer": "Pylance", + "sonarlint.ls.javaHome": "/usr", "sqltools.connections": [ { "database": "postgres", diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 7fe5c2c10..e029f6ce1 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -27,10 +27,10 @@ services: # update to the runtime version. The list of versions available is at # https://hub.docker.com/_/microsoft-vscode-devcontainers # https://mcr.microsoft.com/v2/vscode/devcontainers/python/tags/list - VARIANT: '3.11-bullseye' + VARIANT: '3.14-bookworm' # Node.js version to install - NODE_VERSION: '20.11.1' + NODE_VERSION: '20' context: '..' @@ -41,6 +41,8 @@ services: command: 'sleep infinity' environment: + UV_PROJECT_ENVIRONMENT: '/workspace/api/.venv' + # For container "db". DATABASE_NAME: 'postgres' DATABASE_PASSWORD: 'postgres' @@ -48,7 +50,6 @@ services: # For container "x11" used by Cypress. DISPLAY: ':14' LIBGL_ALWAYS_INDIRECT: '0' - UV_PROJECT_ENVIRONMENT: '/workspace/api/.venv' init: true @@ -72,7 +73,7 @@ services: POSTGRES_PASSWORD: 'postgres' POSTGRES_USER: 'postgres' - image: 'postgres:13.4' + image: 'postgres:16.2' restart: 'unless-stopped' diff --git a/api/migrations/versions/c388edd5dc5a_.py b/api/migrations/versions/c388edd5dc5a_.py new file mode 100644 index 000000000..1637db67b --- /dev/null +++ b/api/migrations/versions/c388edd5dc5a_.py @@ -0,0 +1,129 @@ +"""empty message + +Revision ID: c388edd5dc5a +Revises: 8b6c67545310 +Create Date: 2026-03-27 08:50:06.260961 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'c388edd5dc5a' +down_revision = '8b6c67545310' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('transaction') + with op.batch_alter_table('appointment_version', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_appointment_version_end_transaction_id')) + batch_op.drop_index(batch_op.f('ix_appointment_version_operation_type')) + batch_op.drop_index(batch_op.f('ix_appointment_version_transaction_id')) + + op.drop_table('appointment_version') + with op.batch_alter_table('exam', schema=None) as batch_op: + batch_op.alter_column('expiry_date', + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True) + batch_op.alter_column('exam_received_date', + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True) + batch_op.alter_column('exam_returned_date', + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True) + + with op.batch_alter_table('publicuser', schema=None) as batch_op: + batch_op.alter_column('last_name', + existing_type=sa.VARCHAR(length=200), + type_=sa.String(length=100), + existing_nullable=True) + + with op.batch_alter_table('service', schema=None) as batch_op: + batch_op.alter_column('external_service_name', + existing_type=sa.VARCHAR(length=500), + type_=sa.String(length=100), + existing_nullable=True) + batch_op.alter_column('online_link', + existing_type=sa.VARCHAR(length=500), + type_=sa.String(length=200), + existing_nullable=True) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('service', schema=None) as batch_op: + batch_op.alter_column('online_link', + existing_type=sa.String(length=200), + type_=sa.VARCHAR(length=500), + existing_nullable=True) + batch_op.alter_column('external_service_name', + existing_type=sa.String(length=100), + type_=sa.VARCHAR(length=500), + existing_nullable=True) + + with op.batch_alter_table('publicuser', schema=None) as batch_op: + batch_op.alter_column('last_name', + existing_type=sa.String(length=100), + type_=sa.VARCHAR(length=200), + existing_nullable=True) + + with op.batch_alter_table('exam', schema=None) as batch_op: + batch_op.alter_column('exam_returned_date', + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True) + batch_op.alter_column('exam_received_date', + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True) + batch_op.alter_column('expiry_date', + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True) + + op.create_table('appointment_version', + sa.Column('appointment_id', sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column('office_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('service_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('citizen_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('start_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), + sa.Column('end_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), + sa.Column('checked_in_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), + sa.Column('comments', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('citizen_name', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('contact_information', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('blackout_flag', sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column('recurring_uuid', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('online_flag', sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column('is_draft', sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), + sa.Column('stat_flag', sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('transaction_id', sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column('end_transaction_id', sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column('operation_type', sa.SMALLINT(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('appointment_id', 'transaction_id', name=op.f('appointment_version_pkey')) + ) + with op.batch_alter_table('appointment_version', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_appointment_version_transaction_id'), ['transaction_id'], unique=False) + batch_op.create_index(batch_op.f('ix_appointment_version_operation_type'), ['operation_type'], unique=False) + batch_op.create_index(batch_op.f('ix_appointment_version_end_transaction_id'), ['end_transaction_id'], unique=False) + + op.create_table('transaction', + sa.Column('issued_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.Column('id', sa.BIGINT(), autoincrement=True, nullable=False), + sa.Column('remote_addr', sa.VARCHAR(length=50), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name=op.f('transaction_pkey')) + ) + # ### end Alembic commands ### diff --git a/api/pyproject.toml b/api/pyproject.toml index d21204af5..a2de27e1b 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "queue_api" version = "0.0.0" description = "Queue management API" -requires-python = ">=3.11,<3.12" +requires-python = ">=3.14.0, <3.15" dependencies = [ "alembic>=1.18,<1.19", "amqp>=5.3,<5.4", @@ -49,6 +49,7 @@ dev = [ "pylint>=4.0,<4.1", "pytest>=9.0,<9.1", "pytest-cov>=7.1,<7.2", + "ruff>=0.15.8", ] [tool.setuptools] diff --git a/api/uv.lock b/api/uv.lock index 1f02705dd..be59f17fa 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = "==3.11.*" +requires-python = "==3.14.*" [[package]] name = "alembic" @@ -58,6 +58,16 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, @@ -79,15 +89,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, ] -[[package]] -name = "async-timeout" -version = "5.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, -] - [[package]] name = "attrs" version = "26.1.0" @@ -97,37 +98,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "backports-zstd" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/36a5182ce1d8ef9ef32bff69037bd28b389bbdb66338f8069e61da7028cb/backports_zstd-1.3.0.tar.gz", hash = "sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2", size = 997138, upload-time = "2025-12-29T17:28:06.143Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/28/ed31a0e35feb4538a996348362051b52912d50f00d25c2d388eccef9242c/backports_zstd-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9", size = 435660, upload-time = "2025-12-29T17:25:55.207Z" }, - { url = "https://files.pythonhosted.org/packages/00/0d/3db362169d80442adda9dd563c4f0bb10091c8c1c9a158037f4ecd53988e/backports_zstd-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c", size = 362056, upload-time = "2025-12-29T17:25:56.729Z" }, - { url = "https://files.pythonhosted.org/packages/bd/00/b67ba053a7d6f6dbe2f8a704b7d3a5e01b1d2e2e8edbc9b634f2702ef73c/backports_zstd-1.3.0-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957", size = 505957, upload-time = "2025-12-29T17:25:57.941Z" }, - { url = "https://files.pythonhosted.org/packages/6f/3e/2667c0ddb53ddf28667e330bf9fe92e8e17705a481c9b698e283120565f7/backports_zstd-1.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d", size = 475569, upload-time = "2025-12-29T17:25:59.075Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/4052473217bd954ccdffda5f7264a0e99e7c4ecf70c0f729845c6a45fc5a/backports_zstd-1.3.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2", size = 581196, upload-time = "2025-12-29T17:26:00.508Z" }, - { url = "https://files.pythonhosted.org/packages/e5/bd/064f6fdb61db3d2c473159ebc844243e650dc032de0f8208443a00127925/backports_zstd-1.3.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f", size = 640888, upload-time = "2025-12-29T17:26:02.134Z" }, - { url = "https://files.pythonhosted.org/packages/d8/09/0822403f40932a165a4f1df289d41653683019e4fd7a86b63ed20e9b6177/backports_zstd-1.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b", size = 491100, upload-time = "2025-12-29T17:26:03.418Z" }, - { url = "https://files.pythonhosted.org/packages/a6/a3/f5ac28d74039b7e182a780809dc66b9dbfc893186f5d5444340bba135389/backports_zstd-1.3.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3", size = 565071, upload-time = "2025-12-29T17:26:05.022Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ac/50209aeb92257a642ee987afa1e61d5b6731ab6bf0bff70905856e5aede6/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1", size = 481519, upload-time = "2025-12-29T17:26:06.255Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/b06f64199fb4b2e9437cedbf96d0155ca08aeec35fe81d41065acd44762e/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5", size = 509465, upload-time = "2025-12-29T17:26:07.602Z" }, - { url = "https://files.pythonhosted.org/packages/f4/37/2c365196e61c8fffbbc930ffd69f1ada7aa1c7210857b3e565031c787ac6/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72", size = 585552, upload-time = "2025-12-29T17:26:08.911Z" }, - { url = "https://files.pythonhosted.org/packages/93/8d/c2c4f448bb6b6c9df17410eaedce415e8db0eb25b60d09a3d22a98294d09/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533", size = 562893, upload-time = "2025-12-29T17:26:10.566Z" }, - { url = "https://files.pythonhosted.org/packages/74/e8/2110d4d39115130f7514cbbcec673a885f4052bb68d15e41bc96a7558856/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c", size = 631462, upload-time = "2025-12-29T17:26:11.99Z" }, - { url = "https://files.pythonhosted.org/packages/b9/a8/d64b59ae0714fdace14e43873f794eff93613e35e3e85eead33a4f44cd80/backports_zstd-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f", size = 495125, upload-time = "2025-12-29T17:26:13.218Z" }, - { url = "https://files.pythonhosted.org/packages/ef/d8/bcff0a091fcf27172c57ae463e49d8dec6dc31e01d7e7bf1ae3aad9c3566/backports_zstd-1.3.0-cp311-cp311-win32.whl", hash = "sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e", size = 288664, upload-time = "2025-12-29T17:26:14.791Z" }, - { url = "https://files.pythonhosted.org/packages/28/1a/379061e2abf8c3150ad51c1baab9ac723e01cf7538860a6a74c48f8b73ee/backports_zstd-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4", size = 313633, upload-time = "2025-12-29T17:26:16.31Z" }, - { url = "https://files.pythonhosted.org/packages/35/e7/eca40858883029fc716660106069b23253e2ec5fd34e86b4101c8cfe864b/backports_zstd-1.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120", size = 288814, upload-time = "2025-12-29T17:26:17.571Z" }, - { url = "https://files.pythonhosted.org/packages/9a/d9/8c9c246e5ea79a4f45d551088b11b61f2dc7efcdc5dbe6df3be84a506e0c/backports_zstd-1.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3", size = 409666, upload-time = "2025-12-29T17:27:57.37Z" }, - { url = "https://files.pythonhosted.org/packages/a4/4f/a55b33c314ca8c9074e99daab54d04c5d212070ae7dbc435329baf1b139e/backports_zstd-1.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a", size = 339199, upload-time = "2025-12-29T17:27:58.542Z" }, - { url = "https://files.pythonhosted.org/packages/9d/13/ce31bd048b1c88d0f65d7af60b6cf89cfbed826c7c978f0ebca9a8a71cfc/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943", size = 420332, upload-time = "2025-12-29T17:28:00.332Z" }, - { url = "https://files.pythonhosted.org/packages/cf/80/c0cdbc533d0037b57248588403a3afb050b2a83b8c38aa608e31b3a4d600/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9", size = 393879, upload-time = "2025-12-29T17:28:01.57Z" }, - { url = "https://files.pythonhosted.org/packages/0f/38/c97428867cac058ed196ccaeddfdf82ecd43b8a65965f2950a6e7547e77a/backports_zstd-1.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5", size = 413842, upload-time = "2025-12-29T17:28:03.113Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ec/6247be6536668fe1c7dfae3eaa9c94b00b956b716957c0fc986ba78c3cc4/backports_zstd-1.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a", size = 299684, upload-time = "2025-12-29T17:28:04.856Z" }, -] - [[package]] name = "bidict" version = "0.23.1" @@ -152,16 +122,16 @@ version = "1.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/ef/f285668811a9e1ddb47a18cb0b437d5fc2760d537a2fe8a57875ad6f8448/brotli-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744", size = 863110, upload-time = "2025-11-05T18:38:12.978Z" }, - { url = "https://files.pythonhosted.org/packages/50/62/a3b77593587010c789a9d6eaa527c79e0848b7b860402cc64bc0bc28a86c/brotli-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f", size = 445438, upload-time = "2025-11-05T18:38:14.208Z" }, - { url = "https://files.pythonhosted.org/packages/cd/e1/7fadd47f40ce5549dc44493877db40292277db373da5053aff181656e16e/brotli-1.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd", size = 1534420, upload-time = "2025-11-05T18:38:15.111Z" }, - { url = "https://files.pythonhosted.org/packages/12/8b/1ed2f64054a5a008a4ccd2f271dbba7a5fb1a3067a99f5ceadedd4c1d5a7/brotli-1.2.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe", size = 1632619, upload-time = "2025-11-05T18:38:16.094Z" }, - { url = "https://files.pythonhosted.org/packages/89/5a/7071a621eb2d052d64efd5da2ef55ecdac7c3b0c6e4f9d519e9c66d987ef/brotli-1.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a", size = 1426014, upload-time = "2025-11-05T18:38:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/26/6d/0971a8ea435af5156acaaccec1a505f981c9c80227633851f2810abd252a/brotli-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b", size = 1489661, upload-time = "2025-11-05T18:38:18.41Z" }, - { url = "https://files.pythonhosted.org/packages/f3/75/c1baca8b4ec6c96a03ef8230fab2a785e35297632f402ebb1e78a1e39116/brotli-1.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3", size = 1599150, upload-time = "2025-11-05T18:38:19.792Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1a/23fcfee1c324fd48a63d7ebf4bac3a4115bdb1b00e600f80f727d850b1ae/brotli-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae", size = 1493505, upload-time = "2025-11-05T18:38:20.913Z" }, - { url = "https://files.pythonhosted.org/packages/36/e5/12904bbd36afeef53d45a84881a4810ae8810ad7e328a971ebbfd760a0b3/brotli-1.2.0-cp311-cp311-win32.whl", hash = "sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03", size = 334451, upload-time = "2025-11-05T18:38:21.94Z" }, - { url = "https://files.pythonhosted.org/packages/02/8b/ecb5761b989629a4758c394b9301607a5880de61ee2ee5fe104b87149ebc/brotli-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24", size = 369035, upload-time = "2025-11-05T18:38:22.941Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, ] [[package]] @@ -173,15 +143,16 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/017dc5f852ed9b8735af77774509271acbf1de02d238377667145fcee01d/brotlicffi-1.2.0.1.tar.gz", hash = "sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c", size = 478156, upload-time = "2026-03-05T19:54:11.547Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/f9/dfa56316837fa798eac19358351e974de8e1e2ca9475af4cb90293cd6576/brotlicffi-1.2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd", size = 433046, upload-time = "2026-03-05T19:53:46.209Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f5/f8f492158c76b0d940388801f04f747028971ad5774287bded5f1e53f08d/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5", size = 1541126, upload-time = "2026-03-05T19:53:48.248Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e1/ff87af10ac419600c63e9287a0649c673673ae6b4f2bcf48e96cb2f89f60/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac", size = 1541983, upload-time = "2026-03-05T19:53:50.317Z" }, + { url = "https://files.pythonhosted.org/packages/47/c0/80ecd9bd45776109fab14040e478bf63e456967c9ddee2353d8330ed8de1/brotlicffi-1.2.0.1-cp314-cp314t-win32.whl", hash = "sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec", size = 349047, upload-time = "2026-03-05T19:53:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/ab/98/13e5b250236a281b6cd9e92a01ee1ae231029fa78faee932ef3766e1cb24/brotlicffi-1.2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000", size = 385652, upload-time = "2026-03-05T19:53:53.892Z" }, { url = "https://files.pythonhosted.org/packages/9a/9f/b98dcd4af47994cee97aebac866996a006a2e5fc1fd1e2b82a8ad95cf09c/brotlicffi-1.2.0.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4", size = 432608, upload-time = "2026-03-05T19:53:56.736Z" }, { url = "https://files.pythonhosted.org/packages/b1/7a/ac4ee56595a061e3718a6d1ea7e921f4df156894acffb28ed88a1fd52022/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce", size = 1534257, upload-time = "2026-03-05T19:53:58.667Z" }, { url = "https://files.pythonhosted.org/packages/99/39/e7410db7f6f56de57744ea52a115084ceb2735f4d44973f349bb92136586/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a", size = 1536838, upload-time = "2026-03-05T19:54:00.705Z" }, { url = "https://files.pythonhosted.org/packages/a6/75/6e7977d1935fc3fbb201cbd619be8f2c7aea25d40a096967132854b34708/brotlicffi-1.2.0.1-cp38-abi3-win32.whl", hash = "sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187", size = 343337, upload-time = "2026-03-05T19:54:02.446Z" }, { url = "https://files.pythonhosted.org/packages/d8/ef/e7e485ce5e4ba3843a0a92feb767c7b6098fd6e65ce752918074d175ae71/brotlicffi-1.2.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede", size = 379026, upload-time = "2026-03-05T19:54:04.322Z" }, - { url = "https://files.pythonhosted.org/packages/7f/53/6262c2256513e6f530d81642477cb19367270922063eaa2d7b781d8c723d/brotlicffi-1.2.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851", size = 402265, upload-time = "2026-03-05T19:54:05.858Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d9/d5340b43cf5fbe7fe5a083d237e5338cc1caa73bea523be1c5e452c26290/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf", size = 406710, upload-time = "2026-03-05T19:54:07.272Z" }, - { url = "https://files.pythonhosted.org/packages/a3/82/dbced4c1e0792efdf23fd90ff6d2a320c64ff4dfef7aacc85c04fde9ddd2/brotlicffi-1.2.0.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4", size = 402787, upload-time = "2026-03-05T19:54:08.73Z" }, - { url = "https://files.pythonhosted.org/packages/ef/6f/534205ba7590c9a8716a614f270c5c2ec419b5b7079b3f9cd31b7b5580de/brotlicffi-1.2.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1", size = 375108, upload-time = "2026-03-05T19:54:10.079Z" }, ] [[package]] @@ -211,19 +182,28 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] [[package]] @@ -232,22 +212,38 @@ version = "3.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, - { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, - { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, - { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, - { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, - { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, - { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, - { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, - { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] @@ -278,29 +274,39 @@ version = "7.13.5" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d", size = 219381, upload-time = "2026-03-17T10:30:14.68Z" }, - { url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587", size = 219880, upload-time = "2026-03-17T10:30:16.231Z" }, - { url = "https://files.pythonhosted.org/packages/55/2f/e0e5b237bffdb5d6c530ce87cc1d413a5b7d7dfd60fb067ad6d254c35c76/coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642", size = 250303, upload-time = "2026-03-17T10:30:17.748Z" }, - { url = "https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b", size = 252218, upload-time = "2026-03-17T10:30:19.804Z" }, - { url = "https://files.pythonhosted.org/packages/da/69/2f47bb6fa1b8d1e3e5d0c4be8ccb4313c63d742476a619418f85740d597b/coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686", size = 254326, upload-time = "2026-03-17T10:30:21.321Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d0/79db81da58965bd29dabc8f4ad2a2af70611a57cba9d1ec006f072f30a54/coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743", size = 256267, upload-time = "2026-03-17T10:30:23.094Z" }, - { url = "https://files.pythonhosted.org/packages/e5/32/d0d7cc8168f91ddab44c0ce4806b969df5f5fdfdbb568eaca2dbc2a04936/coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75", size = 250430, upload-time = "2026-03-17T10:30:25.311Z" }, - { url = "https://files.pythonhosted.org/packages/4d/06/a055311d891ddbe231cd69fdd20ea4be6e3603ffebddf8704b8ca8e10a3c/coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209", size = 252017, upload-time = "2026-03-17T10:30:27.284Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f6/d0fd2d21e29a657b5f77a2fe7082e1568158340dceb941954f776dce1b7b/coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a", size = 250080, upload-time = "2026-03-17T10:30:29.481Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ab/0d7fb2efc2e9a5eb7ddcc6e722f834a69b454b7e6e5888c3a8567ecffb31/coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e", size = 253843, upload-time = "2026-03-17T10:30:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/ba/6f/7467b917bbf5408610178f62a49c0ed4377bb16c1657f689cc61470da8ce/coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd", size = 249802, upload-time = "2026-03-17T10:30:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/75/2c/1172fb689df92135f5bfbbd69fc83017a76d24ea2e2f3a1154007e2fb9f8/coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8", size = 250707, upload-time = "2026-03-17T10:30:35.2Z" }, - { url = "https://files.pythonhosted.org/packages/67/21/9ac389377380a07884e3b48ba7a620fcd9dbfaf1d40565facdc6b36ec9ef/coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf", size = 221880, upload-time = "2026-03-17T10:30:36.775Z" }, - { url = "https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9", size = 222816, upload-time = "2026-03-17T10:30:38.891Z" }, - { url = "https://files.pythonhosted.org/packages/12/a6/1d3f6155fb0010ca68eba7fe48ca6c9da7385058b77a95848710ecf189b1/coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028", size = 221483, upload-time = "2026-03-17T10:30:40.463Z" }, + { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, + { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, + { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, + { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, + { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, ] -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - [[package]] name = "cryptography" version = "46.0.6" @@ -324,6 +330,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, + { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, + { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, @@ -338,12 +358,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, - { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, - { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, - { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, ] [[package]] @@ -427,7 +441,6 @@ name = "flask-compress" version = "1.23" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "backports-zstd" }, { name = "brotli", marker = "platform_python_implementation != 'PyPy'" }, { name = "brotlicffi", marker = "platform_python_implementation == 'PyPy'" }, { name = "flask" }, @@ -564,13 +577,14 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/9e/48/b3ef2673ffb940f980966694e40d6d32560f3ffa284ecaeb5ea3a90a6d3f/gevent-25.9.1.tar.gz", hash = "sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd", size = 5059025, upload-time = "2025-09-17T16:15:34.528Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/86/03f8db0704fed41b0fa830425845f1eb4e20c92efa3f18751ee17809e9c6/gevent-25.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7", size = 1792418, upload-time = "2025-09-17T15:41:24.384Z" }, - { url = "https://files.pythonhosted.org/packages/5f/35/f6b3a31f0849a62cfa2c64574bcc68a781d5499c3195e296e892a121a3cf/gevent-25.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457", size = 1875700, upload-time = "2025-09-17T15:48:59.652Z" }, - { url = "https://files.pythonhosted.org/packages/66/1e/75055950aa9b48f553e061afa9e3728061b5ccecca358cef19166e4ab74a/gevent-25.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235", size = 1831365, upload-time = "2025-09-17T15:49:19.426Z" }, - { url = "https://files.pythonhosted.org/packages/31/e8/5c1f6968e5547e501cfa03dcb0239dff55e44c3660a37ec534e32a0c008f/gevent-25.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a", size = 2122087, upload-time = "2025-09-17T15:15:12.329Z" }, - { url = "https://files.pythonhosted.org/packages/c0/2c/ebc5d38a7542af9fb7657bfe10932a558bb98c8a94e4748e827d3823fced/gevent-25.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff", size = 1808776, upload-time = "2025-09-17T15:52:40.16Z" }, - { url = "https://files.pythonhosted.org/packages/e6/26/e1d7d6c8ffbf76fe1fbb4e77bdb7f47d419206adc391ec40a8ace6ebbbf0/gevent-25.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56", size = 2179141, upload-time = "2025-09-17T15:24:09.895Z" }, - { url = "https://files.pythonhosted.org/packages/1d/6c/bb21fd9c095506aeeaa616579a356aa50935165cc0f1e250e1e0575620a7/gevent-25.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586", size = 1677941, upload-time = "2025-09-17T19:59:50.185Z" }, + { url = "https://files.pythonhosted.org/packages/15/1a/948f8167b2cdce573cf01cec07afc64d0456dc134b07900b26ac7018b37e/gevent-25.9.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:1a3fe4ea1c312dbf6b375b416925036fe79a40054e6bf6248ee46526ea628be1", size = 2982934, upload-time = "2025-09-17T14:54:11.302Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ec/726b146d1d3aad82e03d2e1e1507048ab6072f906e83f97f40667866e582/gevent-25.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0adb937f13e5fb90cca2edf66d8d7e99d62a299687400ce2edee3f3504009356", size = 1813982, upload-time = "2025-09-17T15:41:28.506Z" }, + { url = "https://files.pythonhosted.org/packages/35/5d/5f83f17162301662bd1ce702f8a736a8a8cac7b7a35e1d8b9866938d1f9d/gevent-25.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:427f869a2050a4202d93cf7fd6ab5cffb06d3e9113c10c967b6e2a0d45237cb8", size = 1894902, upload-time = "2025-09-17T15:49:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/83/cd/cf5e74e353f60dab357829069ffc300a7bb414c761f52cf8c0c6e9728b8d/gevent-25.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c049880175e8c93124188f9d926af0a62826a3b81aa6d3074928345f8238279e", size = 1861792, upload-time = "2025-09-17T15:49:23.279Z" }, + { url = "https://files.pythonhosted.org/packages/dd/65/b9a4526d4a4edce26fe4b3b993914ec9dc64baabad625a3101e51adb17f3/gevent-25.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5a67a0974ad9f24721034d1e008856111e0535f1541499f72a733a73d658d1c", size = 2113215, upload-time = "2025-09-17T15:15:16.34Z" }, + { url = "https://files.pythonhosted.org/packages/e5/be/7d35731dfaf8370795b606e515d964a0967e129db76ea7873f552045dd39/gevent-25.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d0f5d8d73f97e24ea8d24d8be0f51e0cf7c54b8021c1fddb580bf239474690f", size = 1833449, upload-time = "2025-09-17T15:52:43.75Z" }, + { url = "https://files.pythonhosted.org/packages/65/58/7bc52544ea5e63af88c4a26c90776feb42551b7555a1c89c20069c168a3f/gevent-25.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ddd3ff26e5c4240d3fbf5516c2d9d5f2a998ef87cfb73e1429cfaeaaec860fa6", size = 2176034, upload-time = "2025-09-17T15:24:15.676Z" }, + { url = "https://files.pythonhosted.org/packages/c2/69/a7c4ba2ffbc7c7dbf6d8b4f5d0f0a421f7815d229f4909854266c445a3d4/gevent-25.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:bb63c0d6cb9950cc94036a4995b9cc4667b8915366613449236970f4394f94d7", size = 1703019, upload-time = "2025-09-17T19:30:55.272Z" }, ] [[package]] @@ -579,15 +593,23 @@ version = "3.3.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, - { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, - { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, - { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, - { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5", size = 230389, upload-time = "2026-02-20T20:17:18.772Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a1/65bbc059a43a7e2143ec4fc1f9e3f673e04f9c7b371a494a101422ac4fd5/greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd", size = 229645, upload-time = "2026-02-20T20:18:18.695Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, + { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, + { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, + { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ca/2101ca3d9223a1dc125140dbc063644dca76df6ff356531eb27bc267b446/greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492", size = 232034, upload-time = "2026-02-20T20:20:08.186Z" }, + { url = "https://files.pythonhosted.org/packages/f6/4a/ecf894e962a59dea60f04877eea0fd5724618da89f1867b28ee8b91e811f/greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71", size = 231437, upload-time = "2026-02-20T20:18:59.722Z" }, + { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, + { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, + { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, + { url = "https://files.pythonhosted.org/packages/29/4b/45d90626aef8e65336bed690106d1382f7a43665e2249017e9527df8823b/greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a", size = 237086, upload-time = "2026-02-20T20:20:45.786Z" }, ] [[package]] @@ -728,17 +750,28 @@ version = "3.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] [[package]] @@ -821,17 +854,17 @@ version = "2.9.11" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/8767aaa597ba424643dc87348c6f1754dd9f48e80fdc1b9f7ca5c3a7c213/psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c", size = 379620, upload-time = "2025-10-10T11:14:48.041Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/ae/8d8266f6dd183ab4d48b95b9674034e1b482a3f8619b33a0d86438694577/psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10", size = 3756452, upload-time = "2025-10-10T11:11:11.583Z" }, - { url = "https://files.pythonhosted.org/packages/4b/34/aa03d327739c1be70e09d01182619aca8ebab5970cd0cfa50dd8b9cec2ac/psycopg2_binary-2.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a", size = 3863957, upload-time = "2025-10-10T11:11:16.932Z" }, - { url = "https://files.pythonhosted.org/packages/48/89/3fdb5902bdab8868bbedc1c6e6023a4e08112ceac5db97fc2012060e0c9a/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4", size = 4410955, upload-time = "2025-10-10T11:11:21.21Z" }, - { url = "https://files.pythonhosted.org/packages/ce/24/e18339c407a13c72b336e0d9013fbbbde77b6fd13e853979019a1269519c/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7", size = 4468007, upload-time = "2025-10-10T11:11:24.831Z" }, - { url = "https://files.pythonhosted.org/packages/91/7e/b8441e831a0f16c159b5381698f9f7f7ed54b77d57bc9c5f99144cc78232/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee", size = 4165012, upload-time = "2025-10-10T11:11:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/0d/61/4aa89eeb6d751f05178a13da95516c036e27468c5d4d2509bb1e15341c81/psycopg2_binary-2.9.11-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb", size = 3981881, upload-time = "2025-10-30T02:55:07.332Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/2f5841cae4c635a9459fe7aca8ed771336e9383b6429e05c01267b0774cf/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f", size = 3650985, upload-time = "2025-10-10T11:11:34.975Z" }, - { url = "https://files.pythonhosted.org/packages/84/74/4defcac9d002bca5709951b975173c8c2fa968e1a95dc713f61b3a8d3b6a/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94", size = 3296039, upload-time = "2025-10-10T11:11:40.432Z" }, - { url = "https://files.pythonhosted.org/packages/6d/c2/782a3c64403d8ce35b5c50e1b684412cf94f171dc18111be8c976abd2de1/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f", size = 3043477, upload-time = "2025-10-30T02:55:11.182Z" }, - { url = "https://files.pythonhosted.org/packages/c8/31/36a1d8e702aa35c38fc117c2b8be3f182613faa25d794b8aeaab948d4c03/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908", size = 3345842, upload-time = "2025-10-10T11:11:45.366Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b4/a5375cda5b54cb95ee9b836930fea30ae5a8f14aa97da7821722323d979b/psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03", size = 2713894, upload-time = "2025-10-10T11:11:48.775Z" }, + { url = "https://files.pythonhosted.org/packages/64/12/93ef0098590cf51d9732b4f139533732565704f45bdc1ffa741b7c95fb54/psycopg2_binary-2.9.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1", size = 3756567, upload-time = "2025-10-10T11:13:11.885Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a9/9d55c614a891288f15ca4b5209b09f0f01e3124056924e17b81b9fa054cc/psycopg2_binary-2.9.11-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f", size = 3864755, upload-time = "2025-10-10T11:13:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/13/1e/98874ce72fd29cbde93209977b196a2edae03f8490d1bd8158e7f1daf3a0/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5", size = 4411646, upload-time = "2025-10-10T11:13:24.432Z" }, + { url = "https://files.pythonhosted.org/packages/5a/bd/a335ce6645334fb8d758cc358810defca14a1d19ffbc8a10bd38a2328565/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8", size = 4468701, upload-time = "2025-10-10T11:13:29.266Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/c8b4f53f34e295e45709b7568bf9b9407a612ea30387d35eb9fa84f269b4/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c", size = 4166293, upload-time = "2025-10-10T11:13:33.336Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e0/f8cc36eadd1b716ab36bb290618a3292e009867e5c97ce4aba908cb99644/psycopg2_binary-2.9.11-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f", size = 3983184, upload-time = "2025-10-30T02:55:32.483Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/2a8fe18a4e61cfb3417da67b6318e12691772c0696d79434184a511906dc/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747", size = 3652650, upload-time = "2025-10-10T11:13:38.181Z" }, + { url = "https://files.pythonhosted.org/packages/76/36/03801461b31b29fe58d228c24388f999fe814dfc302856e0d17f97d7c54d/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f", size = 3298663, upload-time = "2025-10-10T11:13:44.878Z" }, + { url = "https://files.pythonhosted.org/packages/97/77/21b0ea2e1a73aa5fa9222b2a6b8ba325c43c3a8d54272839c991f2345656/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b", size = 3044737, upload-time = "2025-10-30T02:55:35.69Z" }, + { url = "https://files.pythonhosted.org/packages/67/69/f36abe5f118c1dca6d3726ceae164b9356985805480731ac6712a63f24f0/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d", size = 3347643, upload-time = "2025-10-10T11:13:53.499Z" }, + { url = "https://files.pythonhosted.org/packages/e1/36/9c0c326fe3a4227953dfb29f5d0c8ae3b8eb8c1cd2967aa569f50cb3c61f/psycopg2_binary-2.9.11-cp314-cp314-win_amd64.whl", hash = "sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316", size = 2803913, upload-time = "2025-10-10T11:13:57.058Z" }, ] [[package]] @@ -928,7 +961,7 @@ name = "pytest-cov" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage", extra = ["toml"] }, + { name = "coverage" }, { name = "pluggy" }, { name = "pytest" }, ] @@ -1043,6 +1076,7 @@ dev = [ { name = "pylint" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "ruff" }, ] [package.metadata] @@ -1088,15 +1122,13 @@ dev = [ { name = "pylint", specifier = ">=4.0,<4.1" }, { name = "pytest", specifier = ">=9.0,<9.1" }, { name = "pytest-cov", specifier = ">=7.1,<7.2" }, + { name = "ruff", specifier = ">=0.15.8" }, ] [[package]] name = "redis" version = "7.4.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, @@ -1109,7 +1141,6 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -1137,33 +1168,35 @@ version = "0.30.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, - { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, - { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, - { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, - { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, - { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, - { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, - { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, - { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, - { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, - { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, - { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, - { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, - { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, ] [[package]] @@ -1178,6 +1211,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +] + [[package]] name = "simple-websocket" version = "1.1.0" @@ -1222,34 +1280,22 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc", size = 2157184, upload-time = "2026-03-02T15:38:28.161Z" }, - { url = "https://files.pythonhosted.org/packages/21/4b/4f3d4a43743ab58b95b9ddf5580a265b593d017693df9e08bd55780af5bb/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c", size = 3313555, upload-time = "2026-03-02T15:58:57.21Z" }, - { url = "https://files.pythonhosted.org/packages/21/dd/3b7c53f1dbbf736fd27041aee68f8ac52226b610f914085b1652c2323442/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7", size = 3313057, upload-time = "2026-03-02T15:52:29.366Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cc/3e600a90ae64047f33313d7d32e5ad025417f09d2ded487e8284b5e21a15/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d", size = 3265431, upload-time = "2026-03-02T15:58:59.096Z" }, - { url = "https://files.pythonhosted.org/packages/8b/19/780138dacfe3f5024f4cf96e4005e91edf6653d53d3673be4844578faf1d/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571", size = 3287646, upload-time = "2026-03-02T15:52:31.569Z" }, - { url = "https://files.pythonhosted.org/packages/40/fd/f32ced124f01a23151f4777e4c705f3a470adc7bd241d9f36a7c941a33bf/sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617", size = 2116956, upload-time = "2026-03-02T15:46:54.535Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c", size = 2141627, upload-time = "2026-03-02T15:46:55.849Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b3/f437eaa1cf028bb3c927172c7272366393e73ccd104dcf5b6963f4ab5318/sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd", size = 2154401, upload-time = "2026-03-02T15:49:17.24Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1c/b3abdf0f402aa3f60f0df6ea53d92a162b458fca2321d8f1f00278506402/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f", size = 3274528, upload-time = "2026-03-02T15:50:41.489Z" }, + { url = "https://files.pythonhosted.org/packages/f2/5e/327428a034407651a048f5e624361adf3f9fbac9d0fa98e981e9c6ff2f5e/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b", size = 3279523, upload-time = "2026-03-02T15:53:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ca/ece73c81a918add0965b76b868b7b5359e068380b90ef1656ee995940c02/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0", size = 3224312, upload-time = "2026-03-02T15:50:42.996Z" }, + { url = "https://files.pythonhosted.org/packages/88/11/fbaf1ae91fa4ee43f4fe79661cead6358644824419c26adb004941bdce7c/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2", size = 3246304, upload-time = "2026-03-02T15:53:34.937Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5fb0deb13930b4f2f698c5541ae076c18981173e27dd00376dbaea7a9c82/sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6", size = 2116565, upload-time = "2026-03-02T15:54:38.321Z" }, + { url = "https://files.pythonhosted.org/packages/95/7e/e83615cb63f80047f18e61e31e8e32257d39458426c23006deeaf48f463b/sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0", size = 2142205, upload-time = "2026-03-02T15:54:39.831Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/69d8711b3f2c5135e9cde5f063bc1605860f0b2c53086d40c04017eb1f77/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241", size = 3563519, upload-time = "2026-03-02T15:57:52.387Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4f/a7cce98facca73c149ea4578981594aaa5fd841e956834931de503359336/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0", size = 3528611, upload-time = "2026-03-02T16:04:42.097Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7d/5936c7a03a0b0cb0fa0cc425998821c6029756b0855a8f7ee70fba1de955/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3", size = 3472326, upload-time = "2026-03-02T15:57:54.423Z" }, + { url = "https://files.pythonhosted.org/packages/f4/33/cea7dfc31b52904efe3dcdc169eb4514078887dff1f5ae28a7f4c5d54b3c/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b", size = 3478453, upload-time = "2026-03-02T16:04:44.584Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/32107c4d13be077a9cae61e9ae49966a35dc4bf442a8852dd871db31f62e/sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f", size = 2147209, upload-time = "2026-03-02T15:52:54.274Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d7/1e073da7a4bc645eb83c76067284a0374e643bc4be57f14cc6414656f92c/sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933", size = 2182198, upload-time = "2026-03-02T15:52:55.606Z" }, { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, ] -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, -] - [[package]] name = "tomlkit" version = "0.14.0" @@ -1346,10 +1392,10 @@ version = "8.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/86/a4/77daa5ba398996d16bb43fc721599d27d03eae68fe3c799de1963c72e228/zope_interface-8.2.tar.gz", hash = "sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224", size = 254019, upload-time = "2026-01-09T07:51:07.253Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/97/9c2aa8caae79915ed64eb114e18816f178984c917aa9adf2a18345e4f2e5/zope_interface-8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322", size = 208081, upload-time = "2026-01-09T08:05:06.623Z" }, - { url = "https://files.pythonhosted.org/packages/34/86/4e2fcb01a8f6780ac84923748e450af0805531f47c0956b83065c99ab543/zope_interface-8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b", size = 208522, upload-time = "2026-01-09T08:05:07.986Z" }, - { url = "https://files.pythonhosted.org/packages/f6/eb/08e277da32ddcd4014922854096cf6dcb7081fad415892c2da1bedefbf02/zope_interface-8.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466", size = 255198, upload-time = "2026-01-09T08:05:09.532Z" }, - { url = "https://files.pythonhosted.org/packages/ea/a1/b32484f3281a5dc83bc713ad61eca52c543735cdf204543172087a074a74/zope_interface-8.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c", size = 259970, upload-time = "2026-01-09T08:05:11.477Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/bca0e8ae1e487d4093a8a7cfed2118aa2d4758c8cfd66e59d2af09d71f1c/zope_interface-8.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce", size = 261153, upload-time = "2026-01-09T08:05:13.402Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/e3ff2a708011e56b10b271b038d4cb650a8ad5b7d24352fe2edf6d6b187a/zope_interface-8.2-cp311-cp311-win_amd64.whl", hash = "sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489", size = 212330, upload-time = "2026-01-09T08:05:15.267Z" }, + { url = "https://files.pythonhosted.org/packages/1a/da/3c89de3917751446728b8898b4d53318bc2f8f6bf8196e150a063c59905e/zope_interface-8.2-cp314-cp314-macosx_10_9_x86_64.whl", hash = "sha256:46c7e4e8cbc698398a67e56ca985d19cb92365b4aafbeb6a712e8c101090f4cb", size = 209223, upload-time = "2026-01-09T08:05:36.449Z" }, + { url = "https://files.pythonhosted.org/packages/00/7f/62d00ec53f0a6e5df0c984781e6f3999ed265129c4c3413df8128d1e0207/zope_interface-8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a87fc7517f825a97ff4a4ca4c8a950593c59e0f8e7bfe1b6f898a38d5ba9f9cf", size = 209366, upload-time = "2026-01-09T08:05:38.197Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a2/f241986315174be8e00aabecfc2153cf8029c1327cab8ed53a9d979d7e08/zope_interface-8.2-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:ccf52f7d44d669203c2096c1a0c2c15d52e36b2e7a9413df50f48392c7d4d080", size = 261037, upload-time = "2026-01-09T08:05:39.568Z" }, + { url = "https://files.pythonhosted.org/packages/02/cc/b321c51d6936ede296a1b8860cf173bee2928357fe1fff7f97234899173f/zope_interface-8.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aae807efc7bd26302eb2fea05cd6de7d59269ed6ae23a6de1ee47add6de99b8c", size = 264219, upload-time = "2026-01-09T08:05:41.624Z" }, + { url = "https://files.pythonhosted.org/packages/ab/fb/5f5e7b40a2f4efd873fe173624795ca47eaa22e29051270c981361b45209/zope_interface-8.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05a0e42d6d830f547e114de2e7cd15750dc6c0c78f8138e6c5035e51ddfff37c", size = 264390, upload-time = "2026-01-09T08:05:42.936Z" }, + { url = "https://files.pythonhosted.org/packages/f9/82/3f2bc594370bc3abd58e5f9085d263bf682a222f059ed46275cde0570810/zope_interface-8.2-cp314-cp314-win_amd64.whl", hash = "sha256:561ce42390bee90bae51cf1c012902a8033b2aaefbd0deed81e877562a116d48", size = 212585, upload-time = "2026-01-09T08:05:44.419Z" }, ] From 9bef1677b7ae51555c644ce00eeff2c706876b7a Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 11:13:23 -0700 Subject: [PATCH 21/81] Update notifications-api deps, add uv workflow --- .vscode/launch.json | 16 + .vscode/tasks.json | 15 + notifications-api/.env.example | 32 + notifications-api/.s2i/environment | 1 - notifications-api/Makefile | 84 +- notifications-api/config.py | 87 +- notifications-api/gunicorn_config.py | 1 - notifications-api/manage.py | 10 +- notifications-api/pyproject.toml | 45 ++ notifications-api/requirements.txt | 312 +++++++- notifications-api/requirements_dev.txt | 405 ++++++++++ notifications-api/setup.cfg | 90 +-- notifications-api/setup.py | 46 +- notifications-api/src/api/__init__.py | 28 +- notifications-api/src/api/app_config.py | 118 +++ .../src/api/resources/__init__.py | 4 +- notifications-api/src/api/resources/email.py | 5 +- notifications-api/src/api/resources/meta.py | 7 +- .../src/api/resources/notifications.py | 5 +- notifications-api/src/api/resources/ops.py | 4 +- .../src/api/services/email/__init__.py | 22 +- .../api/services/email/email_ches_notify.py | 57 +- .../src/api/services/email/email_gc_notify.py | 34 +- .../src/api/services/sms/__init__.py | 17 +- .../src/api/services/sms/custom_notify.py | 1 - .../src/api/services/sms/gc_notify.py | 62 +- notifications-api/tests/conftest.py | 61 ++ notifications-api/tests/test_app.py | 19 + notifications-api/tests/test_config.py | 50 ++ notifications-api/tests/test_notifications.py | 29 + notifications-api/uv.lock | 745 ++++++++++++++++++ notifications-api/wsgi.py | 21 +- 32 files changed, 2030 insertions(+), 403 deletions(-) create mode 100644 notifications-api/.env.example delete mode 100644 notifications-api/.s2i/environment mode change 100755 => 100644 notifications-api/Makefile mode change 100755 => 100644 notifications-api/config.py mode change 100755 => 100644 notifications-api/manage.py create mode 100644 notifications-api/pyproject.toml create mode 100644 notifications-api/requirements_dev.txt mode change 100755 => 100644 notifications-api/setup.cfg mode change 100755 => 100644 notifications-api/setup.py mode change 100755 => 100644 notifications-api/src/api/__init__.py create mode 100644 notifications-api/src/api/app_config.py mode change 100755 => 100644 notifications-api/src/api/services/sms/gc_notify.py create mode 100644 notifications-api/tests/conftest.py create mode 100644 notifications-api/tests/test_app.py create mode 100644 notifications-api/tests/test_config.py create mode 100644 notifications-api/tests/test_notifications.py create mode 100644 notifications-api/uv.lock mode change 100755 => 100644 notifications-api/wsgi.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 050826c84..af7119c93 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -89,6 +89,22 @@ "/**" ], "type": "node" + }, + { + // Run the Notifications API in a single process for reliable debugging. + "cwd": "${workspaceFolder}/notifications-api", + "env": { + "WSGI_DEBUG": "1", + "WSGI_HOST": "0.0.0.0", + "WSGI_PORT": "5002", + "WSGI_USE_RELOADER": "0" + }, + "envFile": "${workspaceFolder}/notifications-api/.env", + "name": "notifications_api", + "program": "${workspaceFolder}/notifications-api/wsgi.py", + "python": "${workspaceFolder}/notifications-api/.venv/bin/python", + "request": "launch", + "type": "debugpy" } ], "version": "0.2.0" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f18a54d31..fd6ec9d7d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,6 +30,21 @@ "reveal": "always" }, "type": "shell" + }, + { + "args": [ + "-c", + "cd ${workspaceFolder}/notifications-api; uv run gunicorn wsgi:application --bind=0.0.0.0:5002 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0" + ], + "command": "bash", + "detail": "uv run gunicorn wsgi:application --bind=0.0.0.0:5002 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0", + "label": "notifications-api: gunicorn hot reload", + "presentation": { + "close": false, + "panel": "new", + "reveal": "always" + }, + "type": "shell" } ], "version": "2.0.0" diff --git a/notifications-api/.env.example b/notifications-api/.env.example new file mode 100644 index 000000000..44d8ba9ed --- /dev/null +++ b/notifications-api/.env.example @@ -0,0 +1,32 @@ +FLASK_CONFIGURATION=development +SECRET_KEY=dev-secret-key +WSGI_HOST=0.0.0.0 +WSGI_PORT=5002 +WSGI_DEBUG=1 +WSGI_USE_RELOADER=0 + +JWT_OIDC_WELL_KNOWN_CONFIG=https://dev.loginproxy.gov.bc.ca/auth/realms/servicebc/.well-known/openid-configuration +JWT_OIDC_ALGORITHMS=RS256 +JWT_OIDC_AUDIENCE=theq-notifications-api +JWT_OIDC_CLIENT_SECRET= +JWT_OIDC_CACHING_ENABLED=true +JWT_OIDC_JWKS_CACHE_TIMEOUT=300 + +SMS_USE_GC_NOTIFY=true +GC_NOTIFY_API_BASE_URL=https://api.notification.canada.ca/ +GC_NOTIFY_API_KEY=replace-me +GC_NOTIFY_SMS_TEMPLATE_ID=replace-me +GC_NOTIFY_EMAIL_TEMPLATE_ID=replace-me +APPOINTMENT_APP_URL=http://localhost:8081 +SMS_APPOINTMENT_APP_URL=http://localhost:8081 +SMS_REMINDER_TEMPLATE=REMINDER: Hi {display_name}, you have an appointment with Service BC {location} on {formatted_date}. To cancel or change, visit {app_url} or call {office_telephone} +SMS_CHECKIN_CONFIRMATION_TEMPLATE=Thank you for saving your spot in line with Service BC! We will be with you shortly. To see your place in line, follow this link, you are ticket {ticket_number}: {url} + +EMAIL_PROVIDER=GC_NOTIFY + +# CHES testing +CHES_POST_EMAIL_ENDPOINT= +CHES_EMAIL_FROM_ID= +CHES_SSO_CLIENT_ID= +CHES_SSO_CLIENT_SECRET= +CHES_SSO_TOKEN_URL= diff --git a/notifications-api/.s2i/environment b/notifications-api/.s2i/environment deleted file mode 100644 index 55c62ce9c..000000000 --- a/notifications-api/.s2i/environment +++ /dev/null @@ -1 +0,0 @@ -APP_CONFIG=gunicorn_config.py diff --git a/notifications-api/Makefile b/notifications-api/Makefile old mode 100755 new mode 100644 index c1ed9ac03..1a100351f --- a/notifications-api/Makefile +++ b/notifications-api/Makefile @@ -1,20 +1,22 @@ -.PHONY: license -.PHONY: setup -.PHONY: ci cd -.PHONY: run +.PHONY: setup sync lock export clean clean-build clean-pyc clean-test lint test run run-hot -MKFILE_PATH:=$(abspath $(lastword $(MAKEFILE_LIST))) -CURRENT_ABS_DIR:=$(patsubst %/,%,$(dir $(MKFILE_PATH))) +UV ?= uv +PORT ?= 5002 -PROJECT_NAME:=pay_api +setup: sync ## Sync the local development environment -################################################################################# -# COMMANDS -- Setup # -################################################################################# -setup: install install-dev ## Setup the project +sync: ## Install/update dependencies into .venv + $(UV) sync --group dev + +lock: ## Refresh the uv lockfile + $(UV) lock + +export: ## Export pinned requirements files for runtime and dev + $(UV) export --frozen --no-dev --format requirements.txt --no-emit-project --output-file requirements.txt + $(UV) export --frozen --group dev --format requirements.txt --no-emit-project --output-file requirements_dev.txt clean: clean-build clean-pyc clean-test ## Clean the project - rm -rf venv/ + rm -rf .venv/ clean-build: ## Clean build files rm -fr build/ @@ -29,60 +31,24 @@ clean-pyc: ## Clean cache files find . -name '*~' -exec rm -f {} + find . -name '__pycache__' -exec rm -fr {} + -clean-test: ## clean test files +clean-test: ## Clean test output find . -name '.pytest_cache' -exec rm -fr {} + - rm -fr .tox/ rm -f .coverage + rm -f coverage.xml rm -fr htmlcov/ -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install-Ur requirements.txt ;\ - touch venv/bin/activate # update so it's as new as requirements/prod.txt - -install-dev: ## Install local application - . venv/bin/activate && pip install -e . - -################################################################################# -# COMMANDS - CI # -################################################################################# -ci: lint flake8 test ## CI flow - -lint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) - -flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests - -test: ## Unit testing - . venv/bin/activate && pytest - -mac-cov: test ## Run the coverage report and display in a browser window (mac) - @open -a "Google Chrome" htmlcov/index.html - -################################################################################# -# COMMANDS - CD # -################################################################################# -cd: build ## CD flow - -build: ## Build the docker container - docker build -t $(PROJECT_NAME) . +lint: ## Lint the project + $(UV) run ruff check . + $(UV) run pylint src/api tests -build-nc: ## Build the docker container without caching - docker build --no-cache -t $(PROJECT_NAME) . +test: ## Run tests + $(UV) run pytest -################################################################################# -# COMMANDS - Local # -################################################################################# -run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 +run: ## Run the project locally + $(UV) run python wsgi.py -################################################################################# -# Self Documenting Commands # -################################################################################# -.PHONY: help +run-hot: ## Run gunicorn with hot reload + $(UV) run gunicorn wsgi:application --bind=0.0.0.0:$(PORT) --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 .DEFAULT_GOAL := help diff --git a/notifications-api/config.py b/notifications-api/config.py old mode 100755 new mode 100644 index 2da039a47..dd3ea8272 --- a/notifications-api/config.py +++ b/notifications-api/config.py @@ -11,89 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""All of the configuration for the service is captured here. All items are loaded, or have Constants defined here that are loaded into the Flask configuration. All modules and lookups get their configuration from the Flask config, rather than reading environment variables directly or by accessing this configuration directly. -""" +"""Compatibility shim for repo-root imports.""" -import os -import sys - -from dotenv import find_dotenv, load_dotenv - -# this will load all the envars from a .env file located in the project root (api) -load_dotenv(find_dotenv()) - -CONFIGURATION = { - 'development': 'config.DevConfig', - 'testing': 'config.TestConfig', - 'production': 'config.ProdConfig', - 'default': 'config.ProdConfig' -} - - -def get_named_config(config_name: str = 'production'): - """Return the configuration object based on the name - - :raise: KeyError: if an unknown configuration is requested - """ - if config_name in ['production', 'staging', 'default']: - config = ProdConfig() - elif config_name == 'testing': - config = TestConfig() - elif config_name == 'development': - config = DevConfig() - else: - raise KeyError(f"Unknown configuration '{config_name}'") - return config - - -class _Config(object): # pylint: disable=too-few-public-methods - """Base class configuration that should set reasonable defaults for all the other configurations. """ - PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) - - # SMS variables - SMS_USE_GC_NOTIFY = os.getenv('SMS_USE_GC_NOTIFY', 'true').lower() == 'true' - # GC Notify - GC_NOTIFY_API_KEY = os.getenv('GC_NOTIFY_API_KEY') - GC_NOTIFY_API_BASE_URL = os.getenv('GC_NOTIFY_API_BASE_URL') - GC_NOTIFY_SMS_TEMPLATE_ID = os.getenv('GC_NOTIFY_SMS_TEMPLATE_ID') - SMS_APPOINTMENT_APP_URL = os.getenv('SMS_APPOINTMENT_APP_URL') - APPOINTMENT_APP_URL = os.getenv('APPOINTMENT_APP_URL') - - # Set up OIDC variables. - SECRET_KEY = os.getenv('SECRET_KEY') - - # JWT_OIDC Settings - JWT_OIDC_WELL_KNOWN_CONFIG = os.getenv('JWT_OIDC_WELL_KNOWN_CONFIG') - JWT_OIDC_ALGORITHMS = os.getenv('JWT_OIDC_ALGORITHMS', 'RS256') - JWT_OIDC_AUDIENCE = os.getenv('JWT_OIDC_AUDIENCE') - JWT_OIDC_CLIENT_SECRET = os.getenv('JWT_OIDC_CLIENT_SECRET', '') - JWT_OIDC_CACHING_ENABLED = os.getenv('JWT_OIDC_CACHING_ENABLED', True) - JWT_OIDC_JWKS_CACHE_TIMEOUT = int(os.getenv('JWT_OIDC_JWKS_CACHE_TIMEOUT', 300)) - - TESTING = False - DEBUG = True - - -class DevConfig(_Config): # pylint: disable=too-few-public-methods - TESTING = False - DEBUG = True - - -class TestConfig(_Config): # pylint: disable=too-few-public-methods - """In support of testing only used by the py.test suite.""" - - DEBUG = True - TESTING = True - - -class ProdConfig(_Config): # pylint: disable=too-few-public-methods - """Production environment configuration.""" - - SECRET_KEY = os.getenv('SECRET_KEY', None) - - if not SECRET_KEY: - SECRET_KEY = os.urandom(24) - print('WARNING: SECRET_KEY being set as a one-shot', file=sys.stderr) - - TESTING = False - DEBUG = False +from api.app_config import * # noqa: F401,F403 diff --git a/notifications-api/gunicorn_config.py b/notifications-api/gunicorn_config.py index 0400cf4e2..bcc8a17fe 100755 --- a/notifications-api/gunicorn_config.py +++ b/notifications-api/gunicorn_config.py @@ -17,7 +17,6 @@ import os - workers = int(os.environ.get('GUNICORN_PROCESSES', '1')) # pylint: disable=invalid-name threads = int(os.environ.get('GUNICORN_THREADS', '1')) # pylint: disable=invalid-name timeout = int(os.environ.get('GUNICORN_TIMEOUT', '30')) # pylint: disable=invalid-name diff --git a/notifications-api/manage.py b/notifications-api/manage.py old mode 100755 new mode 100644 index f92e6e21f..a592112c1 --- a/notifications-api/manage.py +++ b/notifications-api/manage.py @@ -11,15 +11,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Entrypoint for Flask CLI auto-discovery.""" -"""Manage the database and some other items required to run the API -""" import logging from api import create_app - APP = create_app() +app = APP + -if __name__ == '__main__': - logging.log(logging.INFO, 'Running the Manager') +if __name__ == "__main__": + logging.log(logging.INFO, "Running the Manager") diff --git a/notifications-api/pyproject.toml b/notifications-api/pyproject.toml new file mode 100644 index 000000000..5fc6dfc72 --- /dev/null +++ b/notifications-api/pyproject.toml @@ -0,0 +1,45 @@ +[build-system] +requires = ["setuptools>=69", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "notifications_api" +version = "0.0.0" +description = "Service BC notifications API" +readme = "README.md" +requires-python = ">=3.14,<3.15" +dependencies = [ + "Flask>=3.1,<3.2", + "Flask-Caching>=2.3,<2.4", + "flask-jwt-oidc>=0.9,<0.10", + "flask-restx>=1.3,<1.4", + "gunicorn>=25.2,<25.3", + "Jinja2>=3.1,<3.2", + "notifications-python-client>=9.0,<10", + "python-dotenv>=1.2,<1.3", + "requests>=2.33,<2.34", +] + +[dependency-groups] +dev = [ + "pylint>=4.0,<4.1", + "pytest>=9.0,<9.1", + "pytest-cov>=7.1,<7.2", + "ruff>=0.15.8", +] + +[tool.setuptools] +include-package-data = true +py-modules = ["config", "gunicorn_config", "manage", "wsgi"] + +[tool.setuptools.packages.find] +where = ["src"] +include = ["api*"] + +[tool.ruff] +line-length = 120 +target-version = "py314" + +[tool.ruff.lint] +select = ["E", "F", "I", "W"] +ignore = ["E501"] diff --git a/notifications-api/requirements.txt b/notifications-api/requirements.txt index b409a53cd..1c0c6072c 100644 --- a/notifications-api/requirements.txt +++ b/notifications-api/requirements.txt @@ -1,13 +1,299 @@ -Flask -requests -python-dotenv -Jinja2 -pytz -notifications-python-client -Flask-Caching -Werkzeug==2.2.3 -aniso8601 -attrs -flask-restx -gunicorn -flask-jwt-oidc +# This file was autogenerated by uv via the following command: +# uv export --frozen --no-dev --format requirements.txt --no-emit-project --output-file requirements.txt +aniso8601==10.0.1 \ + --hash=sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845 \ + --hash=sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e + # via flask-restx +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # jsonschema + # referencing +blinker==1.9.0 \ + --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ + --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc + # via flask +cachelib==0.13.0 \ + --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ + --hash=sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516 + # via + # flask-caching + # flask-jwt-oidc +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 + # via requests +cffi==2.0.0 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 + # via cryptography +charset-normalizer==3.4.6 \ + --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ + --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ + --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ + --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ + --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ + --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ + --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ + --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ + --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ + --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ + --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ + --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ + --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ + --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ + --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ + --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ + --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ + --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ + --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ + --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ + --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ + --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ + --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ + --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ + --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ + --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ + --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ + --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ + --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ + --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ + --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ + --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ + --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ + --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 + # via requests +click==8.3.1 \ + --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ + --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 + # via flask +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via click +cryptography==46.0.6 \ + --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ + --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ + --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ + --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ + --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ + --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ + --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ + --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ + --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ + --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ + --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ + --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ + --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ + --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ + --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ + --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ + --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ + --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ + --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ + --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ + --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ + --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ + --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ + --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ + --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ + --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ + --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ + --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ + --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ + --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ + --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ + --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ + --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ + --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ + --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ + --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 + # via flask-jwt-oidc +docopt==0.6.2 \ + --hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491 + # via notifications-python-client +flask==3.1.3 \ + --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ + --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c + # via + # flask-caching + # flask-jwt-oidc + # flask-restx + # notifications-api +flask-caching==2.3.1 \ + --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ + --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 + # via notifications-api +flask-jwt-oidc==0.9.0 \ + --hash=sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3 \ + --hash=sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c + # via notifications-api +flask-restx==1.3.2 \ + --hash=sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5 \ + --hash=sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b + # via notifications-api +gunicorn==25.2.0 \ + --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ + --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc + # via notifications-api +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +importlib-resources==6.5.2 \ + --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ + --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec + # via flask-restx +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 + # via flask +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # flask + # notifications-api +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via flask-restx +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +markupsafe==3.0.3 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via + # flask + # jinja2 + # werkzeug +notifications-python-client==9.1.0 \ + --hash=sha256:43b8f738dfa81ae3aa656d91e361dbc51316194f8b9a430706b03347fa7a01bc + # via notifications-api +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 + # via gunicorn +pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via cffi +pyjwt==2.12.1 \ + --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ + --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b + # via + # flask-jwt-oidc + # notifications-python-client +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via notifications-api +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # flask-restx + # jsonschema + # jsonschema-specifications +requests==2.33.0 \ + --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ + --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 + # via + # notifications-api + # notifications-python-client +rpds-py==0.30.0 \ + --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \ + --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \ + --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \ + --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \ + --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \ + --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \ + --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \ + --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \ + --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \ + --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \ + --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \ + --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \ + --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \ + --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \ + --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \ + --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \ + --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \ + --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \ + --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \ + --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \ + --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \ + --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \ + --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \ + --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \ + --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ + --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \ + --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \ + --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \ + --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \ + --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5 + # via + # jsonschema + # referencing +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via requests +werkzeug==3.1.7 \ + --hash=sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f \ + --hash=sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351 + # via + # flask + # flask-restx diff --git a/notifications-api/requirements_dev.txt b/notifications-api/requirements_dev.txt new file mode 100644 index 000000000..7d506a93c --- /dev/null +++ b/notifications-api/requirements_dev.txt @@ -0,0 +1,405 @@ +# This file was autogenerated by uv via the following command: +# uv export --frozen --group dev --format requirements.txt --no-emit-project --output-file requirements_dev.txt +aniso8601==10.0.1 \ + --hash=sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845 \ + --hash=sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e + # via flask-restx +astroid==4.0.4 \ + --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ + --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 + # via pylint +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # jsonschema + # referencing +blinker==1.9.0 \ + --hash=sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf \ + --hash=sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc + # via flask +cachelib==0.13.0 \ + --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ + --hash=sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516 + # via + # flask-caching + # flask-jwt-oidc +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 + # via requests +cffi==2.0.0 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 + # via cryptography +charset-normalizer==3.4.6 \ + --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ + --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ + --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ + --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ + --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ + --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ + --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ + --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ + --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ + --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ + --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ + --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ + --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ + --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ + --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ + --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ + --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ + --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ + --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ + --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ + --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ + --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ + --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ + --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ + --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ + --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ + --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ + --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ + --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ + --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ + --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ + --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ + --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ + --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 + # via requests +click==8.3.1 \ + --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ + --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 + # via flask +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # click + # pylint + # pytest +coverage==7.13.5 \ + --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \ + --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \ + --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \ + --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \ + --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \ + --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \ + --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \ + --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ + --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \ + --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \ + --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \ + --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \ + --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \ + --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \ + --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \ + --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \ + --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \ + --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \ + --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \ + --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \ + --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \ + --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \ + --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \ + --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ + --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \ + --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \ + --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \ + --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \ + --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \ + --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \ + --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \ + --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f + # via pytest-cov +cryptography==46.0.6 \ + --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ + --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ + --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ + --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ + --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ + --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ + --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ + --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ + --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ + --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ + --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ + --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ + --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ + --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ + --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ + --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ + --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ + --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ + --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ + --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ + --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ + --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ + --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ + --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ + --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ + --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ + --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ + --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ + --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ + --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ + --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ + --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ + --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ + --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ + --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ + --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 + # via flask-jwt-oidc +dill==0.4.1 \ + --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ + --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa + # via pylint +docopt==0.6.2 \ + --hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491 + # via notifications-python-client +flask==3.1.3 \ + --hash=sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb \ + --hash=sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c + # via + # flask-caching + # flask-jwt-oidc + # flask-restx + # notifications-api +flask-caching==2.3.1 \ + --hash=sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9 \ + --hash=sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761 + # via notifications-api +flask-jwt-oidc==0.9.0 \ + --hash=sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3 \ + --hash=sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c + # via notifications-api +flask-restx==1.3.2 \ + --hash=sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5 \ + --hash=sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b + # via notifications-api +gunicorn==25.2.0 \ + --hash=sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c \ + --hash=sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc + # via notifications-api +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +importlib-resources==6.5.2 \ + --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ + --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec + # via flask-restx +iniconfig==2.3.0 \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + # via pytest +isort==8.0.1 \ + --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ + --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 + # via pylint +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 + # via flask +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # flask + # notifications-api +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via flask-restx +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +markupsafe==3.0.3 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via + # flask + # jinja2 + # werkzeug +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e + # via pylint +notifications-python-client==9.1.0 \ + --hash=sha256:43b8f738dfa81ae3aa656d91e361dbc51316194f8b9a430706b03347fa7a01bc + # via notifications-api +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 + # via + # gunicorn + # pytest +platformdirs==4.9.4 \ + --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 + # via pylint +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + # via + # pytest + # pytest-cov +pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via cffi +pygments==2.19.2 \ + --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ + --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b + # via pytest +pyjwt==2.12.1 \ + --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ + --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b + # via + # flask-jwt-oidc + # notifications-python-client +pylint==4.0.5 \ + --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ + --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c +pytest==9.0.2 \ + --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ + --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 + # via pytest-cov +pytest-cov==7.1.0 \ + --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ + --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via notifications-api +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # flask-restx + # jsonschema + # jsonschema-specifications +requests==2.33.0 \ + --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ + --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 + # via + # notifications-api + # notifications-python-client +rpds-py==0.30.0 \ + --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \ + --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \ + --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \ + --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \ + --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \ + --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \ + --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \ + --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \ + --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \ + --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \ + --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \ + --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \ + --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \ + --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \ + --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \ + --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \ + --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \ + --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \ + --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \ + --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \ + --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \ + --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \ + --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \ + --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \ + --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ + --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \ + --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \ + --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \ + --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \ + --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5 + # via + # jsonschema + # referencing +ruff==0.15.8 \ + --hash=sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89 \ + --hash=sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1 \ + --hash=sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3 \ + --hash=sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8 \ + --hash=sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762 \ + --hash=sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3 \ + --hash=sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49 \ + --hash=sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb \ + --hash=sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e \ + --hash=sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec \ + --hash=sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34 \ + --hash=sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8 \ + --hash=sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6 \ + --hash=sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7 \ + --hash=sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2 \ + --hash=sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570 \ + --hash=sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a \ + --hash=sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94 +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via pylint +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via requests +werkzeug==3.1.7 \ + --hash=sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f \ + --hash=sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351 + # via + # flask + # flask-restx diff --git a/notifications-api/setup.cfg b/notifications-api/setup.cfg old mode 100755 new mode 100644 index f5cb5d200..7923a24a8 --- a/notifications-api/setup.cfg +++ b/notifications-api/setup.cfg @@ -1,82 +1,14 @@ -[metadata] -name = notifications_api -url = https://github.com/bcgov/queue-management/ -author = Service BC -author_email = -classifiers = - Development Status :: Beta - Intended Audience :: Developers / QA - Topic :: Report - License :: OSI Approved :: Apache Software License - Natural Language :: English - Programming Language :: Python :: 3.7 -license = Apache Software License Version 2.0 -description = A short description of the project -long_description = file: README.md -keywords = - -[options] -zip_safe = True -python_requires = >=3.6 -include_package_data = True -packages = find: - -[options.package_data] -report_api = - -[wheel] -universal = 1 - -[bdist_wheel] -universal = 1 - -[aliases] -test = pytest - -[flake8] -ignore = I001, I003, I004, E126, W504 -exclude = .git,*migrations* -max-line-length = 120 -docstring-min-length=10 -per-file-ignores = - */__init__.py:F401 - -[pycodestyle] -max_line_length = 120 -ignore = E501 -docstring-min-length=10 -notes=FIXME,XXX # TODO is ignored -match_dir = src/api -ignored-modules=flask_sqlalchemy - sqlalchemy -per-file-ignores = - */__init__.py:F401 -good-names= - b, - d, - i, - e, - f, - u, - rv, - logger, - -[pylint] -ignore=migrations,test -max_line_length=120 -notes=FIXME,XXX,TODO -ignored-modules=flask_sqlalchemy,sqlalchemy,SQLAlchemy,alembic,scoped_session -ignored-classes=scoped_session -disable=C0301,W0511 - -[isort] -line_length = 120 -indent = 4 -multi_line_output = 4 -lines_after_imports = 2 - [tool:pytest] -addopts = --cov=src --cov-report html:htmlcov --cov-report xml:coverage.xml -testpaths = tests/unit +addopts = --cov=src --cov-report term-missing --cov-report xml:coverage.xml +testpaths = tests filterwarnings = ignore::UserWarning + +[pylint] +ignore = migrations,test +max_line_length = 120 +notes = FIXME,XXX,TODO +disable = C0301,W0511,R0903 + +[coverage:run] +source = src diff --git a/notifications-api/setup.py b/notifications-api/setup.py old mode 100755 new mode 100644 index 0d7dfd7b1..09e40acf9 --- a/notifications-api/setup.py +++ b/notifications-api/setup.py @@ -11,8 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Installer and setup for this module -""" +"""Compatibility setup.py for legacy source-build flows.""" + from glob import glob from os.path import basename, splitext @@ -20,38 +20,24 @@ def read_requirements(filename): - """ - Get application requirements from - the requirements.txt file. - :return: Python requirements - :rtype: list - """ - with open(filename, 'r') as req: + """Return project requirements from a requirements file.""" + with open(filename, encoding="utf-8") as req: requirements = req.readlines() - install_requires = [r.strip() for r in requirements if r.find('git+') != 0] - return install_requires - - -def read(filepath): - """ - Read the contents from a file. - :param str filepath: path to the file to be read - :return: file contents - :rtype: str - """ - with open(filepath, 'r') as file_handle: - content = file_handle.read() - return content - + return [ + requirement.strip().rstrip("\\").strip() + for requirement in requirements + if requirement.strip() + and not requirement.startswith("#") + and not requirement.startswith(" ") + ] -REQUIREMENTS = read_requirements('requirements.txt') setup( - name="api", - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + name="notifications_api", + packages=find_packages("src"), + package_dir={"": "src"}, + py_modules=[splitext(basename(path))[0] for path in glob("*.py")], include_package_data=True, zip_safe=False, - install_requires=REQUIREMENTS + install_requires=read_requirements("requirements.txt"), ) diff --git a/notifications-api/src/api/__init__.py b/notifications-api/src/api/__init__.py old mode 100755 new mode 100644 index 76e484b78..b94cfd2bb --- a/notifications-api/src/api/__init__.py +++ b/notifications-api/src/api/__init__.py @@ -11,42 +11,41 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""The report Microservice.This module is the API for the Legal Entity system.""" +"""Flask application factory.""" import os + from flask import Flask -import config # pylint: disable=import-error +from api import app_config as config from api.resources import API -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): - """Return a configured Flask App using the Factory method.""" +def create_app(run_mode: str | None = None): + """Return a configured Flask application.""" app = Flask(__name__) - app.config.from_object(config.CONFIGURATION[run_mode]) # pylint: disable=no-member + app.config.from_object(config.get_named_config(run_mode)) API.init_app(app) setup_jwt_manager(app) @app.after_request - def add_version(response): # pylint: disable=unused-variable - version = os.getenv('OPENSHIFT_BUILD_COMMIT', '') - response.headers['API'] = f'notifications_api/{version}' + def add_version(response): + version = os.getenv("OPENSHIFT_BUILD_COMMIT", "") + response.headers["API"] = f"notifications_api/{version}" return response register_shellcontext(app) - return app def setup_jwt_manager(app): - """Use flask app to configure the JWTManager to work for a particular Realm.""" + """Configure the JWT manager for the app.""" from api.auth.auth import jwt as jwt_manager def get_roles(a_dict): - return a_dict['realm_access']['roles'] # pragma: no cover - - app.config['JWT_ROLE_CALLBACK'] = get_roles + return a_dict["realm_access"]["roles"] # pragma: no cover + app.config["JWT_ROLE_CALLBACK"] = get_roles jwt_manager.init_app(app) @@ -54,7 +53,6 @@ def register_shellcontext(app): """Register shell context objects.""" def shell_context(): - """Shell context objects.""" - return {'app': app} # pragma: no cover + return {"app": app} # pragma: no cover app.shell_context_processor(shell_context) diff --git a/notifications-api/src/api/app_config.py b/notifications-api/src/api/app_config.py new file mode 100644 index 000000000..b43b9b4fb --- /dev/null +++ b/notifications-api/src/api/app_config.py @@ -0,0 +1,118 @@ +# Copyright Β© 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Application configuration.""" + +import os +import sys + +from dotenv import find_dotenv, load_dotenv + +load_dotenv(find_dotenv()) + +CONFIGURATION = { + "production": "config.ProdConfig", + "prod": "config.ProdConfig", + "staging": "config.ProdConfig", + "testing": "config.TestConfig", + "test": "config.TestConfig", + "development": "config.DevConfig", + "dev": "config.DevConfig", + "default": "config.ProdConfig", +} + + +def normalize_config_name(config_name: str | None = None) -> str: + """Return the normalized configuration name.""" + requested = ( + config_name + or os.getenv("FLASK_CONFIGURATION") + or os.getenv("FLASK_ENV") + or "default" + ) + normalized = requested.lower() + if normalized not in CONFIGURATION: + raise KeyError(f"Unknown configuration '{requested}'") + return normalized + + +def get_named_config(config_name: str | None = None): + """Return the configuration object based on the name.""" + normalized = normalize_config_name(config_name) + if normalized in {"production", "prod", "staging", "default"}: + return ProdConfig() + if normalized in {"testing", "test"}: + return TestConfig() + return DevConfig() + + +class _Config: # pylint: disable=too-few-public-methods + """Base configuration.""" + + PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + TESTING = False + DEBUG = False + + SECRET_KEY = os.getenv("SECRET_KEY") + + JWT_OIDC_WELL_KNOWN_CONFIG = os.getenv("JWT_OIDC_WELL_KNOWN_CONFIG") + JWT_OIDC_ALGORITHMS = os.getenv("JWT_OIDC_ALGORITHMS", "RS256") + JWT_OIDC_AUDIENCE = os.getenv("JWT_OIDC_AUDIENCE") + JWT_OIDC_CLIENT_SECRET = os.getenv("JWT_OIDC_CLIENT_SECRET", "") + JWT_OIDC_CACHING_ENABLED = os.getenv("JWT_OIDC_CACHING_ENABLED", "true").lower() == "true" + JWT_OIDC_JWKS_CACHE_TIMEOUT = int(os.getenv("JWT_OIDC_JWKS_CACHE_TIMEOUT", "300")) + + SMS_USE_GC_NOTIFY = os.getenv("SMS_USE_GC_NOTIFY", "true").lower() == "true" + GC_NOTIFY_API_KEY = os.getenv("GC_NOTIFY_API_KEY", "") + GC_NOTIFY_API_BASE_URL = os.getenv("GC_NOTIFY_API_BASE_URL", "https://api.notification.canada.ca/") + GC_NOTIFY_SMS_TEMPLATE_ID = os.getenv("GC_NOTIFY_SMS_TEMPLATE_ID", "") + GC_NOTIFY_EMAIL_TEMPLATE_ID = os.getenv("GC_NOTIFY_EMAIL_TEMPLATE_ID", "") + SMS_APPOINTMENT_APP_URL = os.getenv("SMS_APPOINTMENT_APP_URL", "") + APPOINTMENT_APP_URL = os.getenv("APPOINTMENT_APP_URL", "") + SMS_REMINDER_TEMPLATE = os.getenv("SMS_REMINDER_TEMPLATE", "") + SMS_CHECKIN_CONFIRMATION_TEMPLATE = os.getenv("SMS_CHECKIN_CONFIRMATION_TEMPLATE", "") + + EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "GC_NOTIFY") + CHES_SSO_TOKEN_URL = os.getenv("CHES_SSO_TOKEN_URL", "") + CHES_SSO_CLIENT_ID = os.getenv("CHES_SSO_CLIENT_ID", "") + CHES_SSO_CLIENT_SECRET = os.getenv("CHES_SSO_CLIENT_SECRET", "") + CHES_POST_EMAIL_ENDPOINT = os.getenv("CHES_POST_EMAIL_ENDPOINT", "") + CHES_EMAIL_FROM_ID = os.getenv("CHES_EMAIL_FROM_ID", "") + + WSGI_DEBUG = os.getenv("WSGI_DEBUG", "true").lower() in {"1", "true", "yes", "on"} + WSGI_HOST = os.getenv("WSGI_HOST", "0.0.0.0") + WSGI_PORT = int(os.getenv("WSGI_PORT", "5002")) + WSGI_USE_RELOADER = os.getenv("WSGI_USE_RELOADER", "false").lower() in {"1", "true", "yes", "on"} + + +class DevConfig(_Config): # pylint: disable=too-few-public-methods + """Development configuration.""" + + DEBUG = True + + +class TestConfig(_Config): # pylint: disable=too-few-public-methods + """Testing configuration.""" + + DEBUG = True + TESTING = True + + +class ProdConfig(_Config): # pylint: disable=too-few-public-methods + """Production configuration.""" + + SECRET_KEY = os.getenv("SECRET_KEY") + + if not SECRET_KEY: + SECRET_KEY = os.urandom(24) + print("WARNING: SECRET_KEY being set as a one-shot", file=sys.stderr) diff --git a/notifications-api/src/api/resources/__init__.py b/notifications-api/src/api/resources/__init__.py index 9ae967a03..0b517fa78 100755 --- a/notifications-api/src/api/resources/__init__.py +++ b/notifications-api/src/api/resources/__init__.py @@ -23,10 +23,10 @@ from flask_restx import Api +from .email import api as EMAIL_API from .meta import api as META_API -from .ops import api as OPS_API from .notifications import api as NOTIFICATIONS_API -from .email import api as EMAIL_API +from .ops import api as OPS_API # This will add the Authorize button to the swagger docs # oauth2 & openid may not yet be supported by restplus <- check on this diff --git a/notifications-api/src/api/resources/email.py b/notifications-api/src/api/resources/email.py index 988a7361d..692ee4610 100644 --- a/notifications-api/src/api/resources/email.py +++ b/notifications-api/src/api/resources/email.py @@ -13,9 +13,8 @@ # limitations under the License. """Endpoints to check manage notifications.""" -from flask import request, jsonify -from flask_restx import Namespace -from flask_restx import Resource +from flask import jsonify, request +from flask_restx import Namespace, Resource from api.auth.auth import jwt from api.services.email import get_email_service diff --git a/notifications-api/src/api/resources/meta.py b/notifications-api/src/api/resources/meta.py index eaa827803..9cbac8049 100755 --- a/notifications-api/src/api/resources/meta.py +++ b/notifications-api/src/api/resources/meta.py @@ -15,12 +15,11 @@ Currently this only provides API versioning information """ -from flask import jsonify -from flask_restx import cors -from flask_restx import Resource -from flask_restx import Namespace import os +from flask import jsonify +from flask_restx import Namespace, Resource, cors + api = Namespace('', description='API for Sending Service BC Notifications') diff --git a/notifications-api/src/api/resources/notifications.py b/notifications-api/src/api/resources/notifications.py index bed8edfa6..89ad84c09 100644 --- a/notifications-api/src/api/resources/notifications.py +++ b/notifications-api/src/api/resources/notifications.py @@ -13,9 +13,8 @@ # limitations under the License. """Endpoints to check manage notifications.""" -from flask import request, jsonify -from flask_restx import Namespace -from flask_restx import Resource +from flask import jsonify, request +from flask_restx import Namespace, Resource from api.auth.auth import jwt from api.services.sms import get_sms_service diff --git a/notifications-api/src/api/resources/ops.py b/notifications-api/src/api/resources/ops.py index 12e61af0a..c563b611e 100755 --- a/notifications-api/src/api/resources/ops.py +++ b/notifications-api/src/api/resources/ops.py @@ -13,9 +13,7 @@ # limitations under the License. """Endpoints to check and manage the health of the service.""" -from flask_restx import Namespace -from flask_restx import Resource -from flask_restx import cors +from flask_restx import Namespace, Resource, cors api = Namespace('', description='API for Sending Service BC Notifications') diff --git a/notifications-api/src/api/services/email/__init__.py b/notifications-api/src/api/services/email/__init__.py index 4cd6afafa..38895593d 100644 --- a/notifications-api/src/api/services/email/__init__.py +++ b/notifications-api/src/api/services/email/__init__.py @@ -11,20 +11,22 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Email provider selection.""" + +from flask import current_app + from .email_base_service import EmailBaseService -import os def get_email_service(): - """Return SMS Service implementation.""" - from .email_gc_notify import EmailGCNotify + """Return email service implementation.""" from .email_ches_notify import EmailChesNotify + from .email_gc_notify import EmailGCNotify - _instance: EmailBaseService - if os.getenv('EMAIL_PROVIDER') == 'GC_NOTIFY': - _instance = EmailGCNotify() - elif os.getenv('EMAIL_PROVIDER') == 'CHES': - _instance = EmailChesNotify() + provider = current_app.config.get("EMAIL_PROVIDER", "GC_NOTIFY") + instance: EmailBaseService + if provider == "CHES": + instance = EmailChesNotify() else: - _instance = EmailGCNotify() - return _instance + instance = EmailGCNotify() + return instance diff --git a/notifications-api/src/api/services/email/email_ches_notify.py b/notifications-api/src/api/services/email/email_ches_notify.py index 4067253f6..bbea78121 100644 --- a/notifications-api/src/api/services/email/email_ches_notify.py +++ b/notifications-api/src/api/services/email/email_ches_notify.py @@ -11,12 +11,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Send SMS reminder. +"""Send email through CHES.""" -This module is being invoked from a job and it sends SMS reminders to customers. -""" -import os, json, requests -from notifications_python_client import NotificationsAPIClient +import json + +import requests +from flask import current_app from . import EmailBaseService @@ -25,26 +25,35 @@ class EmailChesNotify(EmailBaseService): """Implementation from Ches Email Notify.""" def send(self, email_payload): - """Send SMS reminders for next day appointments.""" - ches_token_url = os.getenv('CHES_SSO_TOKEN_URL') - ches_client_id = os.getenv('CHES_SSO_CLIENT_ID') - ches_client_secret = os.getenv('CHES_SSO_CLIENT_SECRET') - ches_email_endpoint = os.getenv('CHES_POST_EMAIL_ENDPOINT') + """Send email payload through CHES.""" ches_payload = { - 'bodyType': email_payload.get('bodyType'), - 'body': email_payload.get('body'), - 'from': os.getenv('CHES_EMAIL_FROM_ID'), - 'subject': email_payload.get('subject'), - 'to': email_payload.get('to') + "bodyType": email_payload.get("bodyType"), + "body": email_payload.get("body"), + "from": current_app.config["CHES_EMAIL_FROM_ID"], + "subject": email_payload.get("subject"), + "to": email_payload.get("to"), } try: - ches_token_response = requests.post(ches_token_url, - data=f'client_id={ches_client_id}&client_secret={ches_client_secret}&grant_type=client_credentials', - headers={'Content-Type': 'application/x-www-form-urlencoded'}) - ches_api_token = ches_token_response.json().get('access_token') - email_response = requests.post(ches_email_endpoint, - headers={'Content-Type': 'application/json', 'Authorization': f'Bearer {ches_api_token}'}, - data=json.dumps(ches_payload)) + ches_token_response = requests.post( + current_app.config["CHES_SSO_TOKEN_URL"], + data=( + f"client_id={current_app.config['CHES_SSO_CLIENT_ID']}" + f"&client_secret={current_app.config['CHES_SSO_CLIENT_SECRET']}" + "&grant_type=client_credentials" + ), + headers={"Content-Type": "application/x-www-form-urlencoded"}, + timeout=30, + ) + ches_api_token = ches_token_response.json().get("access_token") + email_response = requests.post( + current_app.config["CHES_POST_EMAIL_ENDPOINT"], + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {ches_api_token}", + }, + data=json.dumps(ches_payload), + timeout=30, + ) print(email_response) - except Exception as e: - print(e) # log and continue + except Exception as exc: # pragma: no cover + print(exc) diff --git a/notifications-api/src/api/services/email/email_gc_notify.py b/notifications-api/src/api/services/email/email_gc_notify.py index 6e7c6684b..98639cbf1 100644 --- a/notifications-api/src/api/services/email/email_gc_notify.py +++ b/notifications-api/src/api/services/email/email_gc_notify.py @@ -11,11 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Send SMS reminder. +"""Send email through GC Notify.""" -This module is being invoked from a job and it sends SMS reminders to customers. -""" -import os +from flask import current_app from notifications_python_client import NotificationsAPIClient from . import EmailBaseService @@ -25,21 +23,21 @@ class EmailGCNotify(EmailBaseService): """Implementation for email from GC Notify.""" def send(self, email_payload): - """Send email through GCNotify.""" - api_key = os.getenv('GC_NOTIFY_API_KEY') - gc_notify_url = os.getenv('GC_NOTIFY_API_BASE_URL') - email_template_id = os.getenv('GC_NOTIFY_EMAIL_TEMPLATE_ID') - notifications_client = NotificationsAPIClient(api_key=api_key, base_url=gc_notify_url) - email_to=','.join(email_payload.get('to')) - try: + """Send email through GC Notify.""" + notifications_client = NotificationsAPIClient( + api_key=current_app.config["GC_NOTIFY_API_KEY"], + base_url=current_app.config["GC_NOTIFY_API_BASE_URL"], + ) + email_to = ",".join(email_payload.get("to", [])) + try: response = notifications_client.send_email_notification( email_address=email_to, - template_id=email_template_id, + template_id=current_app.config["GC_NOTIFY_EMAIL_TEMPLATE_ID"], personalisation={ - 'email_subject': email_payload.get('subject'), - 'email_text': email_payload.get('body') - }) + "email_subject": email_payload.get("subject"), + "email_text": email_payload.get("body"), + }, + ) print(response) - - except Exception as e: - print(e) \ No newline at end of file + except Exception as exc: # pragma: no cover + print(exc) diff --git a/notifications-api/src/api/services/sms/__init__.py b/notifications-api/src/api/services/sms/__init__.py index 799c4c0f7..0a436f3f6 100644 --- a/notifications-api/src/api/services/sms/__init__.py +++ b/notifications-api/src/api/services/sms/__init__.py @@ -11,18 +11,21 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""SMS provider selection.""" + +from flask import current_app + from .sms_base_service import SmsBaseService -import os def get_sms_service(): - """Return SMS Service implementation.""" + """Return SMS service implementation.""" from .custom_notify import CustomNotify from .gc_notify import GCNotify - _instance: SmsBaseService - if os.getenv('SMS_USE_GC_NOTIFY', 'true').lower() == 'true': - _instance = GCNotify() + instance: SmsBaseService + if current_app.config.get("SMS_USE_GC_NOTIFY", True): + instance = GCNotify() else: - _instance = CustomNotify() - return _instance + instance = CustomNotify() + return instance diff --git a/notifications-api/src/api/services/sms/custom_notify.py b/notifications-api/src/api/services/sms/custom_notify.py index 461071903..17e8d55f8 100755 --- a/notifications-api/src/api/services/sms/custom_notify.py +++ b/notifications-api/src/api/services/sms/custom_notify.py @@ -15,7 +15,6 @@ This module is being invoked from a job and it sends SMS reminders to customers. """ -import os from typing import Dict from . import SmsBaseService diff --git a/notifications-api/src/api/services/sms/gc_notify.py b/notifications-api/src/api/services/sms/gc_notify.py old mode 100755 new mode 100644 index 245644604..61c3a05f2 --- a/notifications-api/src/api/services/sms/gc_notify.py +++ b/notifications-api/src/api/services/sms/gc_notify.py @@ -11,12 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Send SMS reminder. - -This module is being invoked from a job and it sends SMS reminders to customers. -""" -import os +"""Send SMS reminder.""" +from flask import current_app from notifications_python_client import NotificationsAPIClient from . import SmsBaseService @@ -27,45 +24,36 @@ class GCNotify(SmsBaseService): def send(self, sms_payload): """Send SMS reminders for next day appointments.""" - api_key = os.getenv('GC_NOTIFY_API_KEY') - gc_notify_url = os.getenv('GC_NOTIFY_API_BASE_URL') - gc_template_id = os.getenv('GC_NOTIFY_SMS_TEMPLATE_ID') - - notifications_client = NotificationsAPIClient(api_key=api_key, base_url=gc_notify_url) - sms_requests = sms_payload if type(sms_payload) == list else [sms_payload] + notifications_client = NotificationsAPIClient( + api_key=current_app.config["GC_NOTIFY_API_KEY"], + base_url=current_app.config["GC_NOTIFY_API_BASE_URL"], + ) + sms_requests = sms_payload if isinstance(sms_payload, list) else [sms_payload] for sms_request in sms_requests: try: - if sms_request.get('user_telephone'): + if sms_request.get("user_telephone"): sms_text = self._construct_sms_text(sms_request) - response = notifications_client.send_sms_notification( - phone_number=sms_request.get('user_telephone'), - template_id=gc_template_id, - personalisation={ - 'sms_text': sms_text - }) + phone_number=sms_request.get("user_telephone"), + template_id=current_app.config["GC_NOTIFY_SMS_TEMPLATE_ID"], + personalisation={"sms_text": sms_text}, + ) print(response) - - except Exception as e: - print(e) # log and continue + except Exception as exc: # pragma: no cover + print(exc) @classmethod def _construct_sms_text(cls, sms_request: dict) -> str: """Construct SMS text.""" - message_type: str = sms_request.get('type', 'REMINDER') - template: str = "" - app_url: str = os.getenv('APPOINTMENT_APP_URL') - if message_type == 'REMINDER': - template = os.getenv('SMS_REMINDER_TEMPLATE') - elif message_type == 'CHECKIN_CONFIRMATION': - template = os.getenv('SMS_CHECKIN_CONFIRMATION_TEMPLATE') - elif message_type == 'CUSTOM': - sms_text = sms_request.get('message') - - if template: - sms_text = template.format( - app_url=app_url, - **sms_request - ) - return sms_text + message_type: str = sms_request.get("type", "REMINDER") + template = "" + app_url = current_app.config.get("APPOINTMENT_APP_URL", "") + if message_type == "REMINDER": + template = current_app.config.get("SMS_REMINDER_TEMPLATE", "") + elif message_type == "CHECKIN_CONFIRMATION": + template = current_app.config.get("SMS_CHECKIN_CONFIRMATION_TEMPLATE", "") + elif message_type == "CUSTOM": + return sms_request.get("message", "") + + return template.format(app_url=app_url, **sms_request) if template else "" diff --git a/notifications-api/tests/conftest.py b/notifications-api/tests/conftest.py new file mode 100644 index 000000000..ea1a52bbc --- /dev/null +++ b/notifications-api/tests/conftest.py @@ -0,0 +1,61 @@ +import importlib +import sys + +import pytest + + +@pytest.fixture +def app(monkeypatch): + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.requires_auth", + lambda self, func: func, + raising=False, + ) + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.init_app", + lambda self, app: None, + raising=False, + ) + monkeypatch.setenv("FLASK_CONFIGURATION", "testing") + monkeypatch.setenv("SECRET_KEY", "test-secret") + monkeypatch.setenv( + "JWT_OIDC_WELL_KNOWN_CONFIG", + "https://example.com/.well-known/openid-configuration", + ) + monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") + monkeypatch.setenv("GC_NOTIFY_API_KEY", "test-key") + monkeypatch.setenv("GC_NOTIFY_API_BASE_URL", "https://api.notification.canada.ca/") + monkeypatch.setenv("GC_NOTIFY_SMS_TEMPLATE_ID", "sms-template") + monkeypatch.setenv("GC_NOTIFY_EMAIL_TEMPLATE_ID", "email-template") + monkeypatch.setenv("APPOINTMENT_APP_URL", "http://localhost:8081") + monkeypatch.setenv("SMS_REMINDER_TEMPLATE", "Reminder {display_name} {app_url}") + monkeypatch.setenv( + "SMS_CHECKIN_CONFIRMATION_TEMPLATE", + "Checkin {ticket_number} {url}", + ) + monkeypatch.setenv("EMAIL_PROVIDER", "GC_NOTIFY") + + for module_name in [ + "config", + "api", + "api.auth.auth", + "api.resources", + "api.resources.notifications", + "api.resources.email", + "api.services.sms", + "api.services.email", + ]: + sys.modules.pop(module_name, None) + + from api import create_app + + app = create_app("testing") + app.config.update(TESTING=True) + yield app + + importlib.invalidate_caches() + + +@pytest.fixture +def client(app): + return app.test_client() diff --git a/notifications-api/tests/test_app.py b/notifications-api/tests/test_app.py new file mode 100644 index 000000000..65707f111 --- /dev/null +++ b/notifications-api/tests/test_app.py @@ -0,0 +1,19 @@ +def test_healthz(client): + response = client.get("/api/v1/healthz") + + assert response.status_code == 200 + assert response.get_json() == {"message": "api is healthy"} + + +def test_readyz(client): + response = client.get("/api/v1/readyz") + + assert response.status_code == 200 + assert response.get_json() == {"message": "api is ready"} + + +def test_info_includes_api_header(client): + response = client.get("/api/v1/info") + + assert response.status_code == 200 + assert response.get_json() == {"API": "notifications_api/"} diff --git a/notifications-api/tests/test_config.py b/notifications-api/tests/test_config.py new file mode 100644 index 000000000..797afc19d --- /dev/null +++ b/notifications-api/tests/test_config.py @@ -0,0 +1,50 @@ +import importlib + + +def test_create_app_prefers_flask_configuration(monkeypatch): + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.requires_auth", + lambda self, func: func, + raising=False, + ) + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.init_app", + lambda self, app: None, + raising=False, + ) + monkeypatch.setenv("FLASK_CONFIGURATION", "testing") + monkeypatch.setenv("FLASK_ENV", "production") + monkeypatch.setenv("SECRET_KEY", "test-secret") + monkeypatch.setenv( + "JWT_OIDC_WELL_KNOWN_CONFIG", + "https://example.com/.well-known/openid-configuration", + ) + monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") + + import api + import api.app_config as config + + importlib.reload(config) + importlib.reload(api) + + app = api.create_app() + + assert app.config["TESTING"] is True + + +def test_sms_provider_uses_app_config(app): + from api.services.sms import get_sms_service + from api.services.sms.gc_notify import GCNotify + + with app.app_context(): + app.config["SMS_USE_GC_NOTIFY"] = True + assert isinstance(get_sms_service(), GCNotify) + + +def test_email_provider_uses_app_config(app): + from api.services.email import get_email_service + from api.services.email.email_ches_notify import EmailChesNotify + + with app.app_context(): + app.config["EMAIL_PROVIDER"] = "CHES" + assert isinstance(get_email_service(), EmailChesNotify) diff --git a/notifications-api/tests/test_notifications.py b/notifications-api/tests/test_notifications.py new file mode 100644 index 000000000..99a993242 --- /dev/null +++ b/notifications-api/tests/test_notifications.py @@ -0,0 +1,29 @@ +from unittest.mock import Mock + + +def test_sms_endpoint_calls_selected_service(app, client, monkeypatch): + fake_service = Mock() + monkeypatch.setattr("api.resources.notifications.get_sms_service", lambda: fake_service) + + payload = [{"user_telephone": "+12505551234", "display_name": "Alex"}] + response = client.post("/api/v1/notifications/sms", json=payload) + + assert response.status_code == 200 + fake_service.send.assert_called_once_with(payload) + + +def test_email_endpoint_calls_selected_service(app, client, monkeypatch): + del app + fake_service = Mock() + monkeypatch.setattr("api.resources.email.get_email_service", lambda: fake_service) + + payload = { + "to": ["citizen@example.com"], + "subject": "Hello", + "body": "World", + "bodyType": "text", + } + response = client.post("/api/v1/notifications/email", json=payload) + + assert response.status_code == 200 + fake_service.send.assert_called_once_with(payload) diff --git a/notifications-api/uv.lock b/notifications-api/uv.lock new file mode 100644 index 000000000..6359a2254 --- /dev/null +++ b/notifications-api/uv.lock @@ -0,0 +1,745 @@ +version = 1 +revision = 3 +requires-python = "==3.14.*" + +[[package]] +name = "aniso8601" +version = "10.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/52179c4e3f1978d3d9a285f98c706642522750ef343e9738286130423730/aniso8601-10.0.1.tar.gz", hash = "sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845", size = 47190, upload-time = "2025-04-18T17:29:42.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/75/e0e10dc7ed1408c28e03a6cb2d7a407f99320eb953f229d008a7a6d05546/aniso8601-10.0.1-py2.py3-none-any.whl", hash = "sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e", size = 52848, upload-time = "2025-04-18T17:29:41.492Z" }, +] + +[[package]] +name = "astroid" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "cachelib" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/69/0b5c1259e12fbcf5c2abe5934b5c0c1294ec0f845e2b4b2a51a91d79a4fb/cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48", size = 34418, upload-time = "2024-04-13T14:18:27.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/42/960fc9896ddeb301716fdd554bab7941c35fb90a1dc7260b77df3366f87f/cachelib-0.13.0-py3-none-any.whl", hash = "sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516", size = 20914, upload-time = "2024-04-13T14:18:26.361Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, + { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, + { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, + { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, + { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, + { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, + { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, + { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, + { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, + { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "docopt" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "flask-caching" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachelib" }, + { name = "flask" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/80/74846c8af58ed60972d64f23a6cd0c3ac0175677d7555dff9f51bf82c294/flask_caching-2.3.1.tar.gz", hash = "sha256:65d7fd1b4eebf810f844de7de6258254b3248296ee429bdcb3f741bcbf7b98c9", size = 67560, upload-time = "2025-02-23T01:34:40.207Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/bb/82daa5e2fcecafadcc8659ce5779679d0641666f9252a4d5a2ae987b0506/Flask_Caching-2.3.1-py3-none-any.whl", hash = "sha256:d3efcf600e5925ea5a2fcb810f13b341ae984f5b52c00e9d9070392f3ca10761", size = 28916, upload-time = "2025-02-23T01:34:37.749Z" }, +] + +[[package]] +name = "flask-jwt-oidc" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachelib" }, + { name = "cryptography" }, + { name = "flask" }, + { name = "pyjwt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/59/8827de70afedb42ce19c28aeb2d166e9751538f5b7fa5b6303354ff6fb5d/flask_jwt_oidc-0.9.0.tar.gz", hash = "sha256:6c6169b52b73dbdc06f2e11b9481f382c4125a1b181f88f7270992200cda69a3", size = 7532, upload-time = "2026-03-14T15:53:01.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/08/85dde23b8855e8beb1a5014632fcf8da20242e99669af9fc4c1432bf1a64/flask_jwt_oidc-0.9.0-py3-none-any.whl", hash = "sha256:7ce75f002e7a1ef3639366bf03f62b86e9a3afbd2d1818cbcb71a196b027e55c", size = 9679, upload-time = "2026-03-14T15:53:00.026Z" }, +] + +[[package]] +name = "flask-restx" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aniso8601" }, + { name = "flask" }, + { name = "importlib-resources" }, + { name = "jsonschema" }, + { name = "referencing" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/89/9b9ca58cbb8e9ec46f4a510ba93878e0c88d518bf03c350e3b1b7ad85cbe/flask-restx-1.3.2.tar.gz", hash = "sha256:0ae13d77e7d7e4dce513970cfa9db45364aef210e99022de26d2b73eb4dbced5", size = 2814719, upload-time = "2025-09-23T20:34:25.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/3f/b82cd8e733a355db1abb8297afbf59ec972c00ef90bf8d4eed287958b204/flask_restx-1.3.2-py2.py3-none-any.whl", hash = "sha256:6e035496e8223668044fc45bf769e526352fd648d9e159bd631d94fd645a687b", size = 2799859, upload-time = "2025-09-23T20:34:23.055Z" }, +] + +[[package]] +name = "gunicorn" +version = "25.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/13/dd3f8e40ea3ee907a6cbf3d1f1f81afcc3ecd0087d313baabfe95372f15c/gunicorn-25.2.0.tar.gz", hash = "sha256:10bd7adb36d44945d97d0a1fdf9a0fb086ae9c7b39e56b4dece8555a6bf4a09c", size = 632709, upload-time = "2026-03-24T22:49:54.433Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/53/fb024445837e02cd5cf989cf349bfac6f3f433c05184ea5d49c8ade751c6/gunicorn-25.2.0-py3-none-any.whl", hash = "sha256:88f5b444d0055bf298435384af7294f325e2273fd37ba9f9ff7b98e0a1e5dfdc", size = 211659, upload-time = "2026-03-24T22:49:52.528Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "notifications-api" +version = "0.0.0" +source = { editable = "." } +dependencies = [ + { name = "flask" }, + { name = "flask-caching" }, + { name = "flask-jwt-oidc" }, + { name = "flask-restx" }, + { name = "gunicorn" }, + { name = "jinja2" }, + { name = "notifications-python-client" }, + { name = "python-dotenv" }, + { name = "requests" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pylint" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "flask", specifier = ">=3.1,<3.2" }, + { name = "flask-caching", specifier = ">=2.3,<2.4" }, + { name = "flask-jwt-oidc", specifier = ">=0.9,<0.10" }, + { name = "flask-restx", specifier = ">=1.3,<1.4" }, + { name = "gunicorn", specifier = ">=25.2,<25.3" }, + { name = "jinja2", specifier = ">=3.1,<3.2" }, + { name = "notifications-python-client", specifier = ">=9.0,<10" }, + { name = "python-dotenv", specifier = ">=1.2,<1.3" }, + { name = "requests", specifier = ">=2.33,<2.34" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pylint", specifier = ">=4.0,<4.1" }, + { name = "pytest", specifier = ">=9.0,<9.1" }, + { name = "pytest-cov", specifier = ">=7.1,<7.2" }, + { name = "ruff", specifier = ">=0.15.8" }, +] + +[[package]] +name = "notifications-python-client" +version = "9.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docopt" }, + { name = "pyjwt" }, + { name = "requests" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/4b/3410b0686382778d0e9bdfafa18241a2223447caaa971ad62dde5af2a1a5/notifications_python_client-9.1.0-py3-none-any.whl", hash = "sha256:43b8f738dfa81ae3aa656d91e361dbc51316194f8b9a430706b03347fa7a01bc", size = 9387, upload-time = "2024-05-20T13:27:59.176Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[[package]] +name = "pylint" +version = "4.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/b6/74d9a8a68b8067efce8d07707fe6a236324ee1e7808d2eb3646ec8517c7d/pylint-4.0.5.tar.gz", hash = "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c", size = 1572474, upload-time = "2026-02-20T09:07:33.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "requests" +version = "2.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/43/76ded108b296a49f52de6bac5192ca1c4be84e886f9b5c9ba8427d9694fd/werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351", size = 875700, upload-time = "2026-03-24T01:08:07.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/b2/0bba9bbb4596d2d2f285a16c2ab04118f6b957d8441566e1abb892e6a6b2/werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f", size = 226295, upload-time = "2026-03-24T01:08:06.133Z" }, +] diff --git a/notifications-api/wsgi.py b/notifications-api/wsgi.py old mode 100755 new mode 100644 index 251afc015..ab06296cd --- a/notifications-api/wsgi.py +++ b/notifications-api/wsgi.py @@ -11,11 +11,26 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Provides the WSGI entry point for running the application -""" +"""Provides the WSGI entry point for running the application.""" + from api import create_app application = create_app() + +def _config_flag(name, default=False): + value = application.config.get(name) + if value is None: + return default + if isinstance(value, bool): + return value + return str(value).strip().lower() in {"1", "true", "yes", "on"} + + if __name__ == "__main__": - application.run() + application.run( + host=application.config.get("WSGI_HOST", "0.0.0.0"), + port=int(application.config.get("WSGI_PORT", 5002)), + debug=_config_flag("WSGI_DEBUG", True), + use_reloader=_config_flag("WSGI_USE_RELOADER", False), + ) From a226bddff070c1827eecbcadbcb449568409b446 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 11:38:58 -0700 Subject: [PATCH 22/81] Add log email/sms provider --- .devcontainer/config/api/dotenv | 4 + .vscode/launch.json | 3 +- notifications-api/.env.example | 4 +- notifications-api/src/api/app_config.py | 5 +- .../src/api/services/email/__init__.py | 5 +- .../api/services/email/email_log_notify.py | 14 +++ .../src/api/services/email/payloads.py | 15 +++ .../src/api/services/notification_logging.py | 17 +++ .../src/api/services/sms/__init__.py | 16 ++- .../src/api/services/sms/log_notify.py | 18 +++ .../src/api/services/sms/payloads.py | 29 +++++ notifications-api/tests/conftest.py | 8 ++ notifications-api/tests/test_config.py | 25 ++++- notifications-api/tests/test_notifications.py | 103 +++++++++++++++--- 14 files changed, 245 insertions(+), 21 deletions(-) create mode 100644 notifications-api/src/api/services/email/email_log_notify.py create mode 100644 notifications-api/src/api/services/email/payloads.py create mode 100644 notifications-api/src/api/services/notification_logging.py create mode 100644 notifications-api/src/api/services/sms/log_notify.py create mode 100644 notifications-api/src/api/services/sms/payloads.py diff --git a/.devcontainer/config/api/dotenv b/.devcontainer/config/api/dotenv index 7ac9313a3..d3bdae910 100644 --- a/.devcontainer/config/api/dotenv +++ b/.devcontainer/config/api/dotenv @@ -195,3 +195,7 @@ JWT_OIDC_WELL_KNOWN_CONFIG=https://dev.loginproxy.gov.bc.ca/auth/realms/serviceb #MINIO_BUCKET= #MINIO_USE_SECURE='1' #MINIO_SECRET_KEY= + +############################## Notifications Variables ######################## +NOTIFICATIONS_ENDPOINT=http://localhost:5002/api/v1/notifications/sms +NOTIFICATIONS_EMAIL_ENDPOINT=http://localhost:5002/api/v1/notifications/email diff --git a/.vscode/launch.json b/.vscode/launch.json index af7119c93..d865984a3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,8 @@ "configurations": [ "appointment_front_end", "queue_management_api", - "queue_management_front_end" + "queue_management_front_end", + "notifications_api" ] } ], diff --git a/notifications-api/.env.example b/notifications-api/.env.example index 44d8ba9ed..895557214 100644 --- a/notifications-api/.env.example +++ b/notifications-api/.env.example @@ -12,7 +12,8 @@ JWT_OIDC_CLIENT_SECRET= JWT_OIDC_CACHING_ENABLED=true JWT_OIDC_JWKS_CACHE_TIMEOUT=300 -SMS_USE_GC_NOTIFY=true +EMAIL_PROVIDER=LOG +SMS_PROVIDER=LOG GC_NOTIFY_API_BASE_URL=https://api.notification.canada.ca/ GC_NOTIFY_API_KEY=replace-me GC_NOTIFY_SMS_TEMPLATE_ID=replace-me @@ -22,7 +23,6 @@ SMS_APPOINTMENT_APP_URL=http://localhost:8081 SMS_REMINDER_TEMPLATE=REMINDER: Hi {display_name}, you have an appointment with Service BC {location} on {formatted_date}. To cancel or change, visit {app_url} or call {office_telephone} SMS_CHECKIN_CONFIRMATION_TEMPLATE=Thank you for saving your spot in line with Service BC! We will be with you shortly. To see your place in line, follow this link, you are ticket {ticket_number}: {url} -EMAIL_PROVIDER=GC_NOTIFY # CHES testing CHES_POST_EMAIL_ENDPOINT= diff --git a/notifications-api/src/api/app_config.py b/notifications-api/src/api/app_config.py index b43b9b4fb..1fc0fa2cc 100644 --- a/notifications-api/src/api/app_config.py +++ b/notifications-api/src/api/app_config.py @@ -72,7 +72,8 @@ class _Config: # pylint: disable=too-few-public-methods JWT_OIDC_CACHING_ENABLED = os.getenv("JWT_OIDC_CACHING_ENABLED", "true").lower() == "true" JWT_OIDC_JWKS_CACHE_TIMEOUT = int(os.getenv("JWT_OIDC_JWKS_CACHE_TIMEOUT", "300")) - SMS_USE_GC_NOTIFY = os.getenv("SMS_USE_GC_NOTIFY", "true").lower() == "true" + SMS_PROVIDER = os.getenv("SMS_PROVIDER", "").strip().upper() or "CUSTOM" + SMS_USE_GC_NOTIFY = SMS_PROVIDER == "GC_NOTIFY" GC_NOTIFY_API_KEY = os.getenv("GC_NOTIFY_API_KEY", "") GC_NOTIFY_API_BASE_URL = os.getenv("GC_NOTIFY_API_BASE_URL", "https://api.notification.canada.ca/") GC_NOTIFY_SMS_TEMPLATE_ID = os.getenv("GC_NOTIFY_SMS_TEMPLATE_ID", "") @@ -82,7 +83,7 @@ class _Config: # pylint: disable=too-few-public-methods SMS_REMINDER_TEMPLATE = os.getenv("SMS_REMINDER_TEMPLATE", "") SMS_CHECKIN_CONFIRMATION_TEMPLATE = os.getenv("SMS_CHECKIN_CONFIRMATION_TEMPLATE", "") - EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "GC_NOTIFY") + EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "GC_NOTIFY").strip().upper() CHES_SSO_TOKEN_URL = os.getenv("CHES_SSO_TOKEN_URL", "") CHES_SSO_CLIENT_ID = os.getenv("CHES_SSO_CLIENT_ID", "") CHES_SSO_CLIENT_SECRET = os.getenv("CHES_SSO_CLIENT_SECRET", "") diff --git a/notifications-api/src/api/services/email/__init__.py b/notifications-api/src/api/services/email/__init__.py index 38895593d..5e25c94c1 100644 --- a/notifications-api/src/api/services/email/__init__.py +++ b/notifications-api/src/api/services/email/__init__.py @@ -22,11 +22,14 @@ def get_email_service(): """Return email service implementation.""" from .email_ches_notify import EmailChesNotify from .email_gc_notify import EmailGCNotify + from .email_log_notify import EmailLogNotify - provider = current_app.config.get("EMAIL_PROVIDER", "GC_NOTIFY") + provider = current_app.config.get("EMAIL_PROVIDER", "GC_NOTIFY").upper() instance: EmailBaseService if provider == "CHES": instance = EmailChesNotify() + elif provider == "LOG": + instance = EmailLogNotify() else: instance = EmailGCNotify() return instance diff --git a/notifications-api/src/api/services/email/email_log_notify.py b/notifications-api/src/api/services/email/email_log_notify.py new file mode 100644 index 000000000..7fb8d215c --- /dev/null +++ b/notifications-api/src/api/services/email/email_log_notify.py @@ -0,0 +1,14 @@ +"""Log email payloads instead of sending them.""" + +from api.services.notification_logging import log_notification_payload + +from . import EmailBaseService +from .payloads import build_gc_notify_email_payload + + +class EmailLogNotify(EmailBaseService): + """Implementation that logs GC Notify-shaped email payloads.""" + + def send(self, email_payload): + """Log an email payload.""" + log_notification_payload("email", build_gc_notify_email_payload(email_payload)) diff --git a/notifications-api/src/api/services/email/payloads.py b/notifications-api/src/api/services/email/payloads.py new file mode 100644 index 000000000..9d0ec9a02 --- /dev/null +++ b/notifications-api/src/api/services/email/payloads.py @@ -0,0 +1,15 @@ +"""Payload builders for email providers.""" + +from flask import current_app + + +def build_gc_notify_email_payload(email_payload: dict) -> dict: + """Build the GC Notify email payload shape.""" + return { + "email_address": ",".join(email_payload.get("to", [])), + "template_id": current_app.config["GC_NOTIFY_EMAIL_TEMPLATE_ID"], + "personalisation": { + "email_subject": email_payload.get("subject"), + "email_text": email_payload.get("body"), + }, + } diff --git a/notifications-api/src/api/services/notification_logging.py b/notifications-api/src/api/services/notification_logging.py new file mode 100644 index 000000000..5458bc4eb --- /dev/null +++ b/notifications-api/src/api/services/notification_logging.py @@ -0,0 +1,17 @@ +"""Helpers for logging notification payloads.""" + +import json +import logging +from typing import Any + + +LOGGER = logging.getLogger("api.services.notifications") + + +def log_notification_payload(channel: str, payload: dict[str, Any]) -> None: + """Log a notification payload in a readable, stable format.""" + LOGGER.info( + "%s notification payload:\n%s", + channel.upper(), + json.dumps(payload, indent=2, sort_keys=True), + ) diff --git a/notifications-api/src/api/services/sms/__init__.py b/notifications-api/src/api/services/sms/__init__.py index 0a436f3f6..58122d201 100644 --- a/notifications-api/src/api/services/sms/__init__.py +++ b/notifications-api/src/api/services/sms/__init__.py @@ -18,14 +18,28 @@ from .sms_base_service import SmsBaseService +def get_sms_provider() -> str: + """Return the normalized SMS provider.""" + provider = current_app.config.get("SMS_PROVIDER", "") + normalized = provider.strip().upper() if isinstance(provider, str) else "" + return normalized or "CUSTOM" + + def get_sms_service(): """Return SMS service implementation.""" from .custom_notify import CustomNotify from .gc_notify import GCNotify + from .log_notify import SmsLogNotify + + provider = get_sms_provider() + current_app.config["SMS_PROVIDER"] = provider + current_app.config["SMS_USE_GC_NOTIFY"] = provider == "GC_NOTIFY" instance: SmsBaseService - if current_app.config.get("SMS_USE_GC_NOTIFY", True): + if provider == "GC_NOTIFY": instance = GCNotify() + elif provider == "LOG": + instance = SmsLogNotify() else: instance = CustomNotify() return instance diff --git a/notifications-api/src/api/services/sms/log_notify.py b/notifications-api/src/api/services/sms/log_notify.py new file mode 100644 index 000000000..eb21ef917 --- /dev/null +++ b/notifications-api/src/api/services/sms/log_notify.py @@ -0,0 +1,18 @@ +"""Log SMS payloads instead of sending them.""" + +from api.services.notification_logging import log_notification_payload + +from . import SmsBaseService +from .payloads import build_gc_notify_sms_payload + + +class SmsLogNotify(SmsBaseService): + """Implementation that logs GC Notify-shaped SMS payloads.""" + + def send(self, sms_payload): + """Log SMS reminders.""" + sms_requests = sms_payload if isinstance(sms_payload, list) else [sms_payload] + + for sms_request in sms_requests: + if sms_request.get("user_telephone"): + log_notification_payload("sms", build_gc_notify_sms_payload(sms_request)) diff --git a/notifications-api/src/api/services/sms/payloads.py b/notifications-api/src/api/services/sms/payloads.py new file mode 100644 index 000000000..c554290ec --- /dev/null +++ b/notifications-api/src/api/services/sms/payloads.py @@ -0,0 +1,29 @@ +"""Payload builders for SMS providers.""" + +from flask import current_app + + +def construct_sms_text(sms_request: dict) -> str: + """Construct the SMS text using the current templates.""" + message_type: str = sms_request.get("type", "REMINDER") + template = "" + app_url = current_app.config.get("APPOINTMENT_APP_URL", "") + if message_type == "REMINDER": + template = current_app.config.get("SMS_REMINDER_TEMPLATE", "") + elif message_type == "CHECKIN_CONFIRMATION": + template = current_app.config.get("SMS_CHECKIN_CONFIRMATION_TEMPLATE", "") + elif message_type == "CUSTOM": + return sms_request.get("message", "") + + return template.format(app_url=app_url, **sms_request) if template else "" + + +def build_gc_notify_sms_payload(sms_request: dict) -> dict: + """Build the GC Notify SMS payload shape.""" + return { + "phone_number": sms_request.get("user_telephone"), + "template_id": current_app.config["GC_NOTIFY_SMS_TEMPLATE_ID"], + "personalisation": { + "sms_text": construct_sms_text(sms_request), + }, + } diff --git a/notifications-api/tests/conftest.py b/notifications-api/tests/conftest.py index ea1a52bbc..ce3234583 100644 --- a/notifications-api/tests/conftest.py +++ b/notifications-api/tests/conftest.py @@ -28,6 +28,8 @@ def app(monkeypatch): monkeypatch.setenv("GC_NOTIFY_SMS_TEMPLATE_ID", "sms-template") monkeypatch.setenv("GC_NOTIFY_EMAIL_TEMPLATE_ID", "email-template") monkeypatch.setenv("APPOINTMENT_APP_URL", "http://localhost:8081") + monkeypatch.delenv("SMS_PROVIDER", raising=False) + monkeypatch.delenv("SMS_USE_GC_NOTIFY", raising=False) monkeypatch.setenv("SMS_REMINDER_TEMPLATE", "Reminder {display_name} {app_url}") monkeypatch.setenv( "SMS_CHECKIN_CONFIRMATION_TEMPLATE", @@ -38,12 +40,18 @@ def app(monkeypatch): for module_name in [ "config", "api", + "api.app_config", "api.auth.auth", "api.resources", "api.resources.notifications", "api.resources.email", "api.services.sms", + "api.services.sms.log_notify", + "api.services.sms.payloads", "api.services.email", + "api.services.email.email_log_notify", + "api.services.email.payloads", + "api.services.notification_logging", ]: sys.modules.pop(module_name, None) diff --git a/notifications-api/tests/test_config.py b/notifications-api/tests/test_config.py index 797afc19d..93747aad0 100644 --- a/notifications-api/tests/test_config.py +++ b/notifications-api/tests/test_config.py @@ -34,17 +34,40 @@ def test_create_app_prefers_flask_configuration(monkeypatch): def test_sms_provider_uses_app_config(app): from api.services.sms import get_sms_service + from api.services.sms.custom_notify import CustomNotify from api.services.sms.gc_notify import GCNotify + from api.services.sms.log_notify import SmsLogNotify with app.app_context(): - app.config["SMS_USE_GC_NOTIFY"] = True + app.config.pop("SMS_PROVIDER", None) + assert isinstance(get_sms_service(), CustomNotify) + assert app.config["SMS_USE_GC_NOTIFY"] is False + + app.config["SMS_PROVIDER"] = "GC_NOTIFY" assert isinstance(get_sms_service(), GCNotify) + assert app.config["SMS_USE_GC_NOTIFY"] is True + + app.config["SMS_PROVIDER"] = "LOG" + assert isinstance(get_sms_service(), SmsLogNotify) + assert app.config["SMS_USE_GC_NOTIFY"] is False + + app.config["SMS_PROVIDER"] = "CUSTOM" + assert isinstance(get_sms_service(), CustomNotify) + assert app.config["SMS_USE_GC_NOTIFY"] is False def test_email_provider_uses_app_config(app): from api.services.email import get_email_service from api.services.email.email_ches_notify import EmailChesNotify + from api.services.email.email_gc_notify import EmailGCNotify + from api.services.email.email_log_notify import EmailLogNotify with app.app_context(): + app.config["EMAIL_PROVIDER"] = "GC_NOTIFY" + assert isinstance(get_email_service(), EmailGCNotify) + app.config["EMAIL_PROVIDER"] = "CHES" assert isinstance(get_email_service(), EmailChesNotify) + + app.config["EMAIL_PROVIDER"] = "LOG" + assert isinstance(get_email_service(), EmailLogNotify) diff --git a/notifications-api/tests/test_notifications.py b/notifications-api/tests/test_notifications.py index 99a993242..4e45b1ae9 100644 --- a/notifications-api/tests/test_notifications.py +++ b/notifications-api/tests/test_notifications.py @@ -1,29 +1,106 @@ -from unittest.mock import Mock +def _notification_messages(caplog) -> str: + """Return combined notification log messages.""" + return "\n".join( + record.getMessage() + for record in caplog.records + if record.name == "api.services.notifications" + ) -def test_sms_endpoint_calls_selected_service(app, client, monkeypatch): - fake_service = Mock() - monkeypatch.setattr("api.resources.notifications.get_sms_service", lambda: fake_service) - +def test_sms_endpoint_logs_gc_notify_shaped_payload_for_reminder(app, client, caplog): + del client + app.config["SMS_PROVIDER"] = "LOG" payload = [{"user_telephone": "+12505551234", "display_name": "Alex"}] - response = client.post("/api/v1/notifications/sms", json=payload) + with app.test_client() as test_client, caplog.at_level( + "INFO", logger="api.services.notifications" + ): + response = test_client.post("/api/v1/notifications/sms", json=payload) + + log_output = _notification_messages(caplog) assert response.status_code == 200 - fake_service.send.assert_called_once_with(payload) + assert '"phone_number": "+12505551234"' in log_output + assert '"template_id": "sms-template"' in log_output + assert '"sms_text": "Reminder Alex http://localhost:8081"' in log_output + +def test_sms_endpoint_logs_gc_notify_shaped_payload_for_checkin_confirmation(app, client, caplog): + del client + app.config["SMS_PROVIDER"] = "LOG" + payload = [ + { + "user_telephone": "+12505551234", + "ticket_number": "A123", + "type": "CHECKIN_CONFIRMATION", + "url": "http://localhost:8081/checkin/A123", + } + ] -def test_email_endpoint_calls_selected_service(app, client, monkeypatch): - del app - fake_service = Mock() - monkeypatch.setattr("api.resources.email.get_email_service", lambda: fake_service) + with app.test_client() as test_client, caplog.at_level( + "INFO", logger="api.services.notifications" + ): + response = test_client.post("/api/v1/notifications/sms", json=payload) + + log_output = _notification_messages(caplog) + assert response.status_code == 200 + assert '"phone_number": "+12505551234"' in log_output + assert '"sms_text": "Checkin A123 http://localhost:8081/checkin/A123"' in log_output + +def test_sms_endpoint_logs_gc_notify_shaped_payload_for_custom_sms(app, client, caplog): + del client + app.config["SMS_PROVIDER"] = "LOG" + payload = [ + { + "user_telephone": "+12505551234", + "type": "CUSTOM", + "message": "Bring your ID.", + } + ] + + with app.test_client() as test_client, caplog.at_level( + "INFO", logger="api.services.notifications" + ): + response = test_client.post("/api/v1/notifications/sms", json=payload) + + log_output = _notification_messages(caplog) + assert response.status_code == 200 + assert '"phone_number": "+12505551234"' in log_output + assert '"sms_text": "Bring your ID."' in log_output + + +def test_sms_endpoint_skips_logging_when_phone_number_is_missing(app, client, caplog): + del client + app.config["SMS_PROVIDER"] = "LOG" + payload = [{"display_name": "Alex"}] + + with app.test_client() as test_client, caplog.at_level( + "INFO", logger="api.services.notifications" + ): + response = test_client.post("/api/v1/notifications/sms", json=payload) + + assert response.status_code == 200 + assert _notification_messages(caplog) == "" + + +def test_email_endpoint_logs_gc_notify_shaped_payload(app, client, caplog): + del client + app.config["EMAIL_PROVIDER"] = "LOG" payload = { "to": ["citizen@example.com"], "subject": "Hello", "body": "World", "bodyType": "text", } - response = client.post("/api/v1/notifications/email", json=payload) + with app.test_client() as test_client, caplog.at_level( + "INFO", logger="api.services.notifications" + ): + response = test_client.post("/api/v1/notifications/email", json=payload) + + log_output = _notification_messages(caplog) assert response.status_code == 200 - fake_service.send.assert_called_once_with(payload) + assert '"email_address": "citizen@example.com"' in log_output + assert '"template_id": "email-template"' in log_output + assert '"email_subject": "Hello"' in log_output + assert '"email_text": "World"' in log_output From b5d0106b2ca1089a048ac11c1f95f92e8b4c145e Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 13:26:08 -0700 Subject: [PATCH 23/81] Update README --- README.md | 295 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index e8324b673..73658fecd 100644 --- a/README.md +++ b/README.md @@ -1,183 +1,226 @@ -[![img](https://img.shields.io/badge/Lifecycle-Stable-97ca00)] -## Queue Managment System - About +# The Q -The Queue Managment System will be used to manage the citizen flow and provide analtyics for our Service BC locations. This system is designed to be used for government offices with a large number of services. +The Q is the Service BC platform for managing in-person office flow, appointments, smartboard and digital signage displays, outbound notifications, and related service delivery workflows. The application is used by Service BC as well as other B.C. government ministries. -## Technology Stack Used +Service BC connects people with services offered by the B.C. provincial government. This application supports the day-to-day operations of Service BC locations by helping staff manage queues, appointments, exams, walk-ins, and communications across the province. -Single Signon using KeyCloak. This is used so that we don't need to manage the security concerns of passwords within the application. This also integrates to internal authentication model. +## Applications and Services -Designed for use in an application platform buld for containers specifically OpenShift: +### `api` -- VueJS & BootStrap for Front End -- Flask & Python for API Backend -- EDB (Postgres) -- Redis -- Ngnix +The primary backend service for the platform. It provides the REST API and Socket.IO endpoints used by the staff and public frontends for queue operations, office administration, appointments, walk-ins, exams, uploads, smartboard data, and real-time updates. -## Features +### `frontend` -Designed to accomodate multiple locations. -Designed for both reception based offices and direct counter offices. +The internal staff-facing Vue 2 application. Staff use it for queue management, office administration, appointments, exams, uploads, smartboard support, and optional service-flow integrations. -Additional features for Reception offices: +### `appointment-frontend` -- Waiting queue displayed -- Citizens are called by name -- Digital Signage includes Current number of people waiting -- Handles a Quick Transaction Counter -- Ability to invite next citizen or pick from the waiting queue +The public-facing Vue 2 application for booking appointments, viewing booked appointments, managing account settings, handling sign-in flows, and viewing walk-in queue status. -Basic Digital Signage URLs per office +### `notifications-api` -- Date and Time based on TimeZone -- MP4 to display messageing +A separate Flask service for outbound notifications. It exposes authenticated SMS and email endpoints and supports pluggable delivery providers, including GC Notify, CHES, and logging/custom implementations. -Hold Queue +### `feedback-api` -- Allows staff to place citizen tickets on hold +> [!WARNING] +> `feedback-api` is deprecated, retained only for legacy compatibility, and will be removed in a future version. -Track Channels of an interaction from In Person, Phone, etc. +This legacy Flask service accepts feedback submissions and forwards them to the older Camunda-based feedback flow. -Service Listings +## API Overview -- Sorted by category -- Searching service listings includes descriptions -- Hovering over a service listing displays descriptions -- Ability to customize service listing per Office -- Ability to hide Services from Digital Signage display -- Ability to add multiple services in one interaction +The primary API is served from `api` and exposes endpoints under `/api/v1`. -Office Status Panel +- Queue and citizen service flows: create and manage citizens, service requests, queue state transitions, invite and serve flows, hold flows, and completion flows. +- Office and reference data: offices, services, categories, channels, CSR state, user context, and related administrative data. +- Appointments, bookings, and walk-ins: appointment slots, appointment creation and updates, recurring bookings, walk-in queue support, and reminder-related workflows. +- Exams, rooms, and invigilators: exam scheduling, uploads, exports, room management, and invigilator management. +- Smartboard and real-time updates: smartboard data endpoints plus Socket.IO events for queue changes and office-specific live updates. +- Health endpoints: readiness and health checks for operational monitoring. -- Provides a manager the ability to see counter interaction details +## Supporting APIs -Service Appointments (Optional) +### `notifications-api` -- Calendar for booking appointments -- Ability to Checkin clients and place them at the top of the queue +The notifications service exposes: -Room Booking and Exam Invigilation (Optional) +- `POST /api/v1/notifications/sms` +- `POST /api/v1/notifications/email` -- Manage Industry Trade Authority Group and Individual Exams -- Manage Other (Basic Exams) -- Manage General Room Booking -- Report on Exams +These endpoints are authenticated and are used for outbound text and email delivery. -Basic Administration Panels to add, update and delete: +### `feedback-api` -- Offices -- Customer Service Reps -- Service Listing -- Channels -- Roles -- Invigilators -- Exam Types -- Rooms -- Counter Types +The legacy feedback service exposes: -Feedback +- `POST /api/v1/feedback` -- Sends to Teams and / or Service Now and / or Rocket Chat +It exists for backward compatibility only and should not be treated as a core long-term service. -Analytics +## Frontends -- Key timing events are sent to snowplow for analysis and reporting -- Data is also stored in the Patroni Postgres database as an alternative method to extract analytics +### Staff frontend -## Requirements +The [`frontend`](./frontend) application is the internal operational interface used by Service BC staff. It includes queue dashboards, admin screens, appointment and exam workflows, upload tools, smartboard support, and integrations with other line-of-business applications. -Requires KeyCloak and additional Openshift / Kubernetes Config Maps +### Public appointment frontend -- keycloak.json is required in Front End Container in the following location: /var/www/html/static/keycloak +The [`appointment-frontend`](./appointment-frontend) application is the public web experience. It supports appointment booking, reviewing booked appointments, sign-in and account management, and walk-in queue status pages. - { - "realm": "", - "auth-server-url": "" , - "ssl-required": "", - "resource": "", - "credentials": { - "secret": "" - } - } +## Technology Stack -- secrets.json is required in API Container in the following location: /opt/app-root/src/client_secrets +- Backend: Python, Flask, Flask-RESTX, SQLAlchemy, Flask-Migrate, Flask-SocketIO, Marshmallow, Gunicorn, and Gevent. +- Frontend: Vue 2, TypeScript, Vue Router, Vuex, Vuetify, BootstrapVue, Buefy, and Axios. +- Data and integrations: PostgreSQL, Redis-backed real-time/message queue usage, MinIO for object storage, Keycloak/OIDC authentication, optional Snowplow analytics, and GC Notify/CHES/custom notification providers. +- Serving/runtime: Nginx serves built frontend assets in containerized deployments. - { - "web": { - "realm_public_key": "", - "issuer": "" , - "auth_uri": "" , - "client_id": "", - "client_secret": "", - "redirect_urls": [ - "" - ], - "userinfo_uri": "" , - "token_uri": "" , - "token_introspection_uri": "" - } - } +Older references to RabbitMQ remain in the repository, but they are not part of the current development guidance in this README. -- Digital Signage video (with the name of sbc.mp4) needs to be manually placed in /var/www/html/static/videos +## Development Options -The openshift templates are used for build configs and deployment configs +This repository supports two local development workflows: -Additional Enviornment Variables for API pods are used: +- Devcontainer +- Local host -TEAMS_URL - to integrate feedback to Teams -THEQ_SNOWPLOW_ENDPOINT - where snowplow events are sent -THEQ_SNOWPLOW_APPID - Application ID for snowplow -THEQ_SNOWPLOW_NAMESPACE - Snowplow events namespace -THEQ_SNOWPLOW_CALLFLAG - disable/enable snowplow (Value: True or False) +### Dev Container Workflow -## [Installation](documentation/Readme.md) +#### Prerequisites -Additional information can be found in the [documention](documentation/Readme.md) folder. +- Podman or another compatible Docker engine +- Visual Studio Code (or another editor with Dev Container support) +- The Dev Containers extension for VS Code -## Getting Help or Reporting an Issue +#### Steps -To report bugs/issues/feature requests, please file an [issue](../../issues). +1. Open the repository in VS Code. +2. Use the Dev Containers command to reopen the project in the devcontainer. +3. Let the post-create script finish installing Python and Node dependencies. +4. Confirm that the container has provisioned PostgreSQL and forwarded the main ports. -## How to Contribute +#### Expected Ports -_If you are including a Code of Conduct, make sure that you have a [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) file, and include the following text in here in the README:_ -"Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms." +- `5000`: queue management API +- `5002`: notifications API +- `8080`: staff frontend +- `8081`: appointment frontend +- `5432`: PostgreSQL -## License +The devcontainer installs dependencies automatically, applies database migrations, and may initialize seed data depending on the current database state. + +### Local Host Workflow + +#### Prerequisites + +- Python 3.14 +- `uv` +- Node.js 20 +- `npm` +- PostgreSQL 16 or a compatible local PostgreSQL instance + +#### Setup + +1. Install backend dependencies: + + ```bash + cd ./api + uv sync --group dev + + cd ./notifications-api + uv sync --group dev + ``` + +2. Install frontend dependencies: + + ```bash + cd ./frontend + npm install + + cd ./appointment-frontend + npm install + ``` + +3. Create the required local config files from the repo's devcontainer config sources: + + ```bash + cp ./.devcontainer/config/api/dotenv ./api/.env + cp ./.devcontainer/config/api/client_secrets/secrets.json ./api/client_secrets/secrets.json + cp ./.devcontainer/config/frontend/public/static/keycloak/keycloak.json ./frontend/public/static/keycloak/keycloak.json + cp ./.devcontainer/config/frontend/public/config/configuration.json ./frontend/public/config/configuration.json + cp ./.devcontainer/config/appointment-frontend/dotenv.local ./appointment-frontend/.env.local + cp ./.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json ./appointment-frontend/public/config/kc/keycloak-public.json + cp ./.devcontainer/config/appointment-frontend/public/config/configuration.json ./appointment-frontend/public/config/configuration.json + ``` + +4. Start PostgreSQL and make sure the database settings in `api/.env` point to your local database. -Detailed guidance around licenses is available -[here](/BC-Open-Source-Development-Employee-Guide/Licenses.md) +5. Run database migrations: -Attach the appropriate LICENSE file directly into your repository before you do anything else! + ```bash + cd ./api + uv run python manage.py db upgrade + ``` -The default license For code repositories is: Apache 2.0 +#### Run Commands -Here is the boiler-plate you should put into the comments header of every source code file as well as the bottom of your README.md: +Start the services in separate terminals. - Copyright 2015 Province of British Columbia +Queue management API: - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +```bash +cd ./api +uv run gunicorn wsgi --bind=0.0.0.0:5000 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 +``` - http://www.apache.org/licenses/LICENSE-2.0 +Notifications API: - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +```bash +cd ./notifications-api +uv run gunicorn wsgi:application --bind=0.0.0.0:5002 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 +``` -For repos that are made up of docs, wikis and non-code stuff it's Creative Commons Attribution 4.0 International, and should look like this at the bottom of your README.md: +Staff frontend: -Creative Commons Licence
YOUR REPO NAME HERE by the Province of Britich Columbia is licensed under a Creative Commons Attribution 4.0 International License. +```bash +cd ./frontend +npm run serve +``` -and the code for the cc 4.0 footer looks like this: +Appointment frontend: + +```bash +cd ./appointment-frontend +npm run serve -- --port 8081 +``` + +#### Local Config Files + +These are the main local files you should expect to have in place when running the application locally: + +- `api/.env` +- `api/client_secrets/secrets.json` +- `frontend/public/static/keycloak/keycloak.json` +- `frontend/public/config/configuration.json` +- `appointment-frontend/.env.local` +- `appointment-frontend/public/config/kc/keycloak-public.json` +- `appointment-frontend/public/config/configuration.json` + +## Deployment + +For deployment within the B.C. government, this project can be hosted on the [B.C. government Private Cloud](https://digital.gov.bc.ca/technology/cloud/private/). The platform is the B.C. Government Private Cloud PaaS, powered by Red Hat OpenShift, and is designed for hosting government applications in a managed private-cloud environment. + +This repository still includes deployment artifacts under [`openshift/templates`](./openshift/templates) for platform-specific builds and deployments. + +## Getting Help or Reporting an Issue + +To report bugs/issues/feature requests, please file an [issue](../../issues). + +## How to Contribute + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +## License - Creative Commons Licence
YOUR REPO NAME HERE by the Province of Britich Columbia - is licensed under a - Creative Commons Attribution 4.0 International License. \ No newline at end of file +This project is licensed under the Apache License, Version 2.0. See the root [`LICENSE`](./LICENSE) file for the full license text. From 09aa123b9b171810f99e5e3abd9805b226ab5ffa Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 17:21:06 -0700 Subject: [PATCH 24/81] Fix alembic migrations --- api/app/models/bookings/__init__.py | 3 +- api/app/models/bookings/versioning.py | 50 +++++ api/migrations/versions/c388edd5dc5a_.py | 270 +++++++++++++++-------- 3 files changed, 226 insertions(+), 97 deletions(-) create mode 100644 api/app/models/bookings/versioning.py diff --git a/api/app/models/bookings/__init__.py b/api/app/models/bookings/__init__.py index e446176fb..b1fab2b64 100644 --- a/api/app/models/bookings/__init__.py +++ b/api/app/models/bookings/__init__.py @@ -19,4 +19,5 @@ from app.models.bookings.exam_type import ExamType from app.models.bookings.invigilator import Invigilator from app.models.bookings.room import Room -from app.models.bookings.appointments import Appointment \ No newline at end of file +from app.models.bookings.versioning import appointment_version, transaction +from app.models.bookings.appointments import Appointment diff --git a/api/app/models/bookings/versioning.py b/api/app/models/bookings/versioning.py new file mode 100644 index 000000000..130368700 --- /dev/null +++ b/api/app/models/bookings/versioning.py @@ -0,0 +1,50 @@ +"""Metadata definitions for manual appointment versioning tables.""" + +from sqlalchemy import DateTime, Index, PrimaryKeyConstraint + +from qsystem import db + + +appointment_version = db.Table( + "appointment_version", + db.metadata, + db.Column("appointment_id", db.Integer, nullable=False), + db.Column("office_id", db.Integer, nullable=True), + db.Column("service_id", db.Integer, nullable=True), + db.Column("citizen_id", db.Integer, nullable=True), + db.Column("start_time", DateTime(timezone=True), nullable=True), + db.Column("end_time", DateTime(timezone=True), nullable=True), + db.Column("checked_in_time", DateTime(timezone=True), nullable=True), + db.Column("comments", db.String(255), nullable=True), + db.Column("citizen_name", db.String(255), nullable=True), + db.Column("contact_information", db.String(255), nullable=True), + db.Column("blackout_flag", db.String(1), nullable=True), + db.Column("recurring_uuid", db.String(255), nullable=True), + db.Column("online_flag", db.Boolean(), nullable=True), + db.Column("is_draft", db.Boolean(), nullable=True), + db.Column("created_at", DateTime(timezone=True), nullable=True), + db.Column("stat_flag", db.Boolean(), nullable=True), + db.Column("updated_at", DateTime(timezone=True), nullable=True), + db.Column("updated_by", db.String(), nullable=True), + db.Column("transaction_id", db.BigInteger, nullable=False), + db.Column("end_transaction_id", db.BigInteger, nullable=True), + db.Column("operation_type", db.SmallInteger, nullable=False), + PrimaryKeyConstraint( + "appointment_id", + "transaction_id", + name="appointment_version_pkey", + ), + Index("ix_appointment_version_transaction_id", "transaction_id"), + Index("ix_appointment_version_operation_type", "operation_type"), + Index("ix_appointment_version_end_transaction_id", "end_transaction_id"), +) + + +transaction = db.Table( + "transaction", + db.metadata, + db.Column("issued_at", DateTime(), nullable=True), + db.Column("id", db.BigInteger, autoincrement=True, nullable=False), + db.Column("remote_addr", db.String(50), nullable=True), + PrimaryKeyConstraint("id", name="transaction_pkey"), +) diff --git a/api/migrations/versions/c388edd5dc5a_.py b/api/migrations/versions/c388edd5dc5a_.py index 1637db67b..36dcb9143 100644 --- a/api/migrations/versions/c388edd5dc5a_.py +++ b/api/migrations/versions/c388edd5dc5a_.py @@ -5,125 +5,203 @@ Create Date: 2026-03-27 08:50:06.260961 """ -from alembic import op + import sqlalchemy as sa import sqlalchemy_utc +from alembic import op from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = 'c388edd5dc5a' -down_revision = '8b6c67545310' +revision = "c388edd5dc5a" +down_revision = "8b6c67545310" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('transaction') - with op.batch_alter_table('appointment_version', schema=None) as batch_op: - batch_op.drop_index(batch_op.f('ix_appointment_version_end_transaction_id')) - batch_op.drop_index(batch_op.f('ix_appointment_version_operation_type')) - batch_op.drop_index(batch_op.f('ix_appointment_version_transaction_id')) - - op.drop_table('appointment_version') - with op.batch_alter_table('exam', schema=None) as batch_op: - batch_op.alter_column('expiry_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True) - batch_op.alter_column('exam_received_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True) - batch_op.alter_column('exam_returned_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True) + with op.batch_alter_table("exam", schema=None) as batch_op: + batch_op.alter_column( + "expiry_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True, + ) + batch_op.alter_column( + "exam_received_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True, + ) + batch_op.alter_column( + "exam_returned_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True, + ) - with op.batch_alter_table('publicuser', schema=None) as batch_op: - batch_op.alter_column('last_name', - existing_type=sa.VARCHAR(length=200), - type_=sa.String(length=100), - existing_nullable=True) + with op.batch_alter_table("publicuser", schema=None) as batch_op: + batch_op.alter_column( + "last_name", + existing_type=sa.VARCHAR(length=200), + type_=sa.String(length=100), + existing_nullable=True, + ) - with op.batch_alter_table('service', schema=None) as batch_op: - batch_op.alter_column('external_service_name', - existing_type=sa.VARCHAR(length=500), - type_=sa.String(length=100), - existing_nullable=True) - batch_op.alter_column('online_link', - existing_type=sa.VARCHAR(length=500), - type_=sa.String(length=200), - existing_nullable=True) + with op.batch_alter_table("service", schema=None) as batch_op: + batch_op.alter_column( + "external_service_name", + existing_type=sa.VARCHAR(length=500), + type_=sa.String(length=100), + existing_nullable=True, + ) + batch_op.alter_column( + "online_link", + existing_type=sa.VARCHAR(length=500), + type_=sa.String(length=200), + existing_nullable=True, + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('service', schema=None) as batch_op: - batch_op.alter_column('online_link', - existing_type=sa.String(length=200), - type_=sa.VARCHAR(length=500), - existing_nullable=True) - batch_op.alter_column('external_service_name', - existing_type=sa.String(length=100), - type_=sa.VARCHAR(length=500), - existing_nullable=True) + with op.batch_alter_table("service", schema=None) as batch_op: + batch_op.alter_column( + "online_link", + existing_type=sa.String(length=200), + type_=sa.VARCHAR(length=500), + existing_nullable=True, + ) + batch_op.alter_column( + "external_service_name", + existing_type=sa.String(length=100), + type_=sa.VARCHAR(length=500), + existing_nullable=True, + ) - with op.batch_alter_table('publicuser', schema=None) as batch_op: - batch_op.alter_column('last_name', - existing_type=sa.String(length=100), - type_=sa.VARCHAR(length=200), - existing_nullable=True) + with op.batch_alter_table("publicuser", schema=None) as batch_op: + batch_op.alter_column( + "last_name", + existing_type=sa.String(length=100), + type_=sa.VARCHAR(length=200), + existing_nullable=True, + ) - with op.batch_alter_table('exam', schema=None) as batch_op: - batch_op.alter_column('exam_returned_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True) - batch_op.alter_column('exam_received_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True) - batch_op.alter_column('expiry_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True) + with op.batch_alter_table("exam", schema=None) as batch_op: + batch_op.alter_column( + "exam_returned_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True, + ) + batch_op.alter_column( + "exam_received_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True, + ) + batch_op.alter_column( + "expiry_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True, + ) - op.create_table('appointment_version', - sa.Column('appointment_id', sa.INTEGER(), autoincrement=False, nullable=False), - sa.Column('office_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('service_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('citizen_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('start_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), - sa.Column('end_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), - sa.Column('checked_in_time', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), - sa.Column('comments', sa.VARCHAR(length=255), autoincrement=False, nullable=True), - sa.Column('citizen_name', sa.VARCHAR(length=255), autoincrement=False, nullable=True), - sa.Column('contact_information', sa.VARCHAR(length=255), autoincrement=False, nullable=True), - sa.Column('blackout_flag', sa.VARCHAR(length=1), autoincrement=False, nullable=True), - sa.Column('recurring_uuid', sa.VARCHAR(length=255), autoincrement=False, nullable=True), - sa.Column('online_flag', sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column('is_draft', sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), - sa.Column('stat_flag', sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True), - sa.Column('updated_by', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('transaction_id', sa.BIGINT(), autoincrement=False, nullable=False), - sa.Column('end_transaction_id', sa.BIGINT(), autoincrement=False, nullable=True), - sa.Column('operation_type', sa.SMALLINT(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('appointment_id', 'transaction_id', name=op.f('appointment_version_pkey')) + op.create_table( + "appointment_version", + sa.Column("appointment_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("office_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("service_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("citizen_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "start_time", + postgresql.TIMESTAMP(timezone=True), + autoincrement=False, + nullable=True, + ), + sa.Column( + "end_time", + postgresql.TIMESTAMP(timezone=True), + autoincrement=False, + nullable=True, + ), + sa.Column( + "checked_in_time", + postgresql.TIMESTAMP(timezone=True), + autoincrement=False, + nullable=True, + ), + sa.Column( + "comments", sa.VARCHAR(length=255), autoincrement=False, nullable=True + ), + sa.Column( + "citizen_name", sa.VARCHAR(length=255), autoincrement=False, nullable=True + ), + sa.Column( + "contact_information", + sa.VARCHAR(length=255), + autoincrement=False, + nullable=True, + ), + sa.Column( + "blackout_flag", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "recurring_uuid", sa.VARCHAR(length=255), autoincrement=False, nullable=True + ), + sa.Column("online_flag", sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column("is_draft", sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column( + "created_at", + postgresql.TIMESTAMP(timezone=True), + autoincrement=False, + nullable=True, + ), + sa.Column("stat_flag", sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.Column( + "updated_at", + postgresql.TIMESTAMP(timezone=True), + autoincrement=False, + nullable=True, + ), + sa.Column("updated_by", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("transaction_id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column( + "end_transaction_id", sa.BIGINT(), autoincrement=False, nullable=True + ), + sa.Column("operation_type", sa.SMALLINT(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint( + "appointment_id", "transaction_id", name=op.f("appointment_version_pkey") + ), ) - with op.batch_alter_table('appointment_version', schema=None) as batch_op: - batch_op.create_index(batch_op.f('ix_appointment_version_transaction_id'), ['transaction_id'], unique=False) - batch_op.create_index(batch_op.f('ix_appointment_version_operation_type'), ['operation_type'], unique=False) - batch_op.create_index(batch_op.f('ix_appointment_version_end_transaction_id'), ['end_transaction_id'], unique=False) + with op.batch_alter_table("appointment_version", schema=None) as batch_op: + batch_op.create_index( + batch_op.f("ix_appointment_version_transaction_id"), + ["transaction_id"], + unique=False, + ) + batch_op.create_index( + batch_op.f("ix_appointment_version_operation_type"), + ["operation_type"], + unique=False, + ) + batch_op.create_index( + batch_op.f("ix_appointment_version_end_transaction_id"), + ["end_transaction_id"], + unique=False, + ) - op.create_table('transaction', - sa.Column('issued_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), - sa.Column('id', sa.BIGINT(), autoincrement=True, nullable=False), - sa.Column('remote_addr', sa.VARCHAR(length=50), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name=op.f('transaction_pkey')) + op.create_table( + "transaction", + sa.Column( + "issued_at", postgresql.TIMESTAMP(), autoincrement=False, nullable=True + ), + sa.Column("id", sa.BIGINT(), autoincrement=True, nullable=False), + sa.Column( + "remote_addr", sa.VARCHAR(length=50), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("id", name=op.f("transaction_pkey")), ) # ### end Alembic commands ### From 5a7a596de8a8613ba5577a710f7683b6c18ecb35 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 27 Mar 2026 17:21:23 -0700 Subject: [PATCH 25/81] Fix utc warning after upgrade --- api/app/models/bookings/appointments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/models/bookings/appointments.py b/api/app/models/bookings/appointments.py index 09c90f127..b74d13ca3 100644 --- a/api/app/models/bookings/appointments.py +++ b/api/app/models/bookings/appointments.py @@ -179,7 +179,7 @@ def _record_appointment_version(connection, appointment, operation_type): "INSERT INTO transaction (issued_at, remote_addr) " "VALUES (:issued_at, :remote_addr) RETURNING id" ), - {"issued_at": datetime.utcnow(), "remote_addr": None}, + {"issued_at": utcnow().replace(tzinfo=None), "remote_addr": None}, ).scalar() connection.execute( From 8302071b3f090f3ac491875434b078b420d8be46 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Mon, 30 Mar 2026 11:40:39 -0700 Subject: [PATCH 26/81] Update Keycloak config for local dev --- .devcontainer/config/api/dotenv | 2 +- .../public/config/kc/keycloak-public.json | 6 +- .../public/static/keycloak/keycloak.json | 4 +- .devcontainer/postCreateCommand.sh | 9 +- README.md | 23 +- appointment-frontend/README.md | 3 +- compose.yaml | 16 + documentation/demo-files/keycloak.json | 9 +- frontend/config/dev.env.js | 2 +- keycloak-local-testserver/Dockerfile | 23 - keycloak-local-testserver/registry-realm.json | 1951 ----------------- keycloak-local-testserver/setup.sh | 43 - keycloak-local-testserver/startup.sh | 3 - keycloak-local/servicebc-local-realm.json | 555 +++++ 14 files changed, 609 insertions(+), 2040 deletions(-) create mode 100644 compose.yaml delete mode 100644 keycloak-local-testserver/Dockerfile delete mode 100644 keycloak-local-testserver/registry-realm.json delete mode 100755 keycloak-local-testserver/setup.sh delete mode 100755 keycloak-local-testserver/startup.sh create mode 100644 keycloak-local/servicebc-local-realm.json diff --git a/.devcontainer/config/api/dotenv b/.devcontainer/config/api/dotenv index d3bdae910..8fd4dc7dd 100644 --- a/.devcontainer/config/api/dotenv +++ b/.devcontainer/config/api/dotenv @@ -184,7 +184,7 @@ SQLALCHEMY_ECHO=False ############################# JWT Config Variables ############################ JWT_OIDC_AUDIENCE=theq-queue-management-api -JWT_OIDC_WELL_KNOWN_CONFIG=https://dev.loginproxy.gov.bc.ca/auth/realms/servicebc/.well-known/openid-configuration +JWT_OIDC_WELL_KNOWN_CONFIG=http://localhost:8085/auth/realms/servicebc-local/.well-known/openid-configuration ########################## Exam Management Variables ########################## #BCMP_BASE_URL= diff --git a/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json b/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json index 43d6027a9..870b14ef5 100644 --- a/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json +++ b/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json @@ -1,8 +1,8 @@ { - "realm": "servicebc", - "auth-server-url": "https://dev.loginproxy.gov.bc.ca/auth/", + "realm": "servicebc-local", + "auth-server-url": "http://localhost:8085/auth", "ssl-required": "external", - "resource": "theq-frontend", + "resource": "theq-appointment-frontend", "public-client": "true", "confidential-port": 0 } diff --git a/.devcontainer/config/frontend/public/static/keycloak/keycloak.json b/.devcontainer/config/frontend/public/static/keycloak/keycloak.json index 43d6027a9..51c903744 100644 --- a/.devcontainer/config/frontend/public/static/keycloak/keycloak.json +++ b/.devcontainer/config/frontend/public/static/keycloak/keycloak.json @@ -1,6 +1,6 @@ { - "realm": "servicebc", - "auth-server-url": "https://dev.loginproxy.gov.bc.ca/auth/", + "realm": "servicebc-local", + "auth-server-url": "http://localhost:8085/auth", "ssl-required": "external", "resource": "theq-frontend", "public-client": "true", diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index c9077ee62..ae1e341e5 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -144,17 +144,16 @@ bootstrap_database () { ( cd api export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv" + SEARCH_USER=admin uv run python manage.py db upgrade - # If there is nothing in the CSR table, we're probably starting with a - # clean database and need to bootstrap it with default data. + # If the default bootstrap admin CSR is missing, we're probably + # starting with a clean database and need to bootstrap it. uv run python manage.py migrate_db - read -p "Enter your IDIR to check if db is bootstrapped: " SEARCH_USER COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ - -U postgres -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';" -t) + -U postgres -tA -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';") if [ "$COUNT" -eq 0 ]; then uv run python manage.py bootstrap - echo "$SEARCH_USER" | uv run python manage.py adduser fi ) } diff --git a/README.md b/README.md index 73658fecd..89bda694b 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,26 @@ The devcontainer installs dependencies automatically, applies database migration cp ./.devcontainer/config/appointment-frontend/public/config/configuration.json ./appointment-frontend/public/config/configuration.json ``` -4. Start PostgreSQL and make sure the database settings in `api/.env` point to your local database. +4. Start the local auth server and PostgreSQL: -5. Run database migrations: + ```bash + docker compose up -d keycloak + ``` + + Local Keycloak details: + + - Realm: `servicebc-local` + - Base URL: `http://localhost:8085/auth` + - Admin console: `http://localhost:8085/auth/admin/` + - Admin credentials: `admin` / `password` + - Demo users: `csr@idir`, `ga@idir`, `support@idir`, `citizen@bceidboth` + - Demo user password: `password` + - Confidential client id: `theq-queue-management-api` + - Confidential client secret: `theq-local-dev-secret` + +5. Make sure the database settings in `api/.env` point to your local database. + +6. Run database migrations: ```bash cd ./api @@ -207,6 +224,8 @@ These are the main local files you should expect to have in place when running t - `appointment-frontend/public/config/kc/keycloak-public.json` - `appointment-frontend/public/config/configuration.json` +The checked-in local auth defaults now target the local Keycloak realm on `http://localhost:8085/auth`. If you need to switch back to the shared dev Keycloak server, update the copied local config files before starting the apps. + ## Deployment For deployment within the B.C. government, this project can be hosted on the [B.C. government Private Cloud](https://digital.gov.bc.ca/technology/cloud/private/). The platform is the B.C. Government Private Cloud PaaS, powered by Red Hat OpenShift, and is designed for hosting government applications in a managed private-cloud environment. diff --git a/appointment-frontend/README.md b/appointment-frontend/README.md index 49bbdca99..ebe44b94b 100644 --- a/appointment-frontend/README.md +++ b/appointment-frontend/README.md @@ -19,7 +19,8 @@ cp -r .devcontainer/config/api/client_secrets api/ ``` Note: -- inorder to book an appointment, add a user to the local keycloak with the role ***'online_appointment_user'***. +- a local Keycloak realm named `servicebc-local` is available through `docker compose up -d keycloak` +- the seeded demo appointment user is `citizen@bceidboth` with password `password` - if you need to use BCSC or BCeID services, please point to the dev keycloak diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..0dfab38ee --- /dev/null +++ b/compose.yaml @@ -0,0 +1,16 @@ +services: + keycloak: + image: quay.io/keycloak/keycloak:26.2.2 + command: + - start-dev + - --import-realm + environment: + KC_DB: dev-mem + KC_HTTP_RELATIVE_PATH: /auth + KC_HEALTH_ENABLED: "true" + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: password + ports: + - "8085:8080" + volumes: + - ./keycloak-local:/opt/keycloak/data/import:ro diff --git a/documentation/demo-files/keycloak.json b/documentation/demo-files/keycloak.json index 6d657e43d..870b14ef5 100644 --- a/documentation/demo-files/keycloak.json +++ b/documentation/demo-files/keycloak.json @@ -1,9 +1,8 @@ { - "realm": "registry", + "realm": "servicebc-local", "auth-server-url": "http://localhost:8085/auth", "ssl-required": "external", - "resource": "account", - "credentials": { - "secret": "5abdcb03-9dc6-4789-8c1f-8230c7d7cb79" - } + "resource": "theq-appointment-frontend", + "public-client": "true", + "confidential-port": 0 } diff --git a/frontend/config/dev.env.js b/frontend/config/dev.env.js index c9558f850..5cdbe609b 100644 --- a/frontend/config/dev.env.js +++ b/frontend/config/dev.env.js @@ -5,6 +5,6 @@ module.exports = merge(prodEnv, { NODE_ENV: '"localhost"', API_URL: '"http://localhost:5000/api/v1"', SOCKET_URL: '"http://localhost:5000"', - KEYCLOAK_JSON_URL: '"http://localhost:8080/static/keycloak.json"', + KEYCLOAK_JSON_URL: '"http://localhost:8080/static/keycloak/keycloak.json"', REFRESH_TOKEN_SECONDS_LEFT: 180 }) diff --git a/keycloak-local-testserver/Dockerfile b/keycloak-local-testserver/Dockerfile deleted file mode 100644 index 5735b0e7e..000000000 --- a/keycloak-local-testserver/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM centos:7 -# derived from Roy Willemse original work - -RUN yum -y install java-1.8.0-openjdk.x86_64 && \ - curl -L -s -S https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 -o /usr/local/sbin/jq && \ - chmod +x /usr/local/sbin/jq && \ - groupadd -r keycloak -g 1000 && \ - useradd -u 1000 -r -g keycloak -m -d /opt/keycloak -s /sbin/nologin -c "Keycloak user" keycloak && \ - chmod 755 /opt/keycloak - -USER keycloak - -ENV KEYCLOAK_VERSION 3.4.3.Final -ENV KEYCLOAK_HOME /opt/keycloak/keycloak-$KEYCLOAK_VERSION - -RUN curl https://download.jboss.org/keycloak/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz | tar -C $HOME -zx -RUN $KEYCLOAK_HOME/bin/add-user-keycloak.sh -r master -u admin -p admin - -ADD . /opt/keycloak - -RUN /opt/keycloak/setup.sh - -CMD ["/opt/keycloak/startup.sh"] diff --git a/keycloak-local-testserver/registry-realm.json b/keycloak-local-testserver/registry-realm.json deleted file mode 100644 index ad2a89d28..000000000 --- a/keycloak-local-testserver/registry-realm.json +++ /dev/null @@ -1,1951 +0,0 @@ -{ - "id": "registry", - "realm": "registry", - "notBefore": 1491571623, - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 300, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "offlineSessionIdleTimeout": 2592000, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 300, - "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, - "roles": { - "realm": [ - { - "id": "7e7056d9-66ae-40d6-80f5-e87469f2be15", - "name": "ADMIN", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "13a958fc-58d2-4308-a56e-fa4241c4fb03", - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "336ca58f-3005-4723-a87c-36aaff2e6f71", - "name": "USER", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "1d8bfb2e-54c3-4598-aa98-8a97c22914fc", - "name": "editor", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "5e4710f4-f53d-4477-a69c-282d8afbb613", - "name": "online_appointment_user", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "21463f18-976a-43a1-b12e-71244b13f34f", - "name": "offline_access", - "description": "${role_offline-access}", - "scopeParamRequired": true, - "composite": false, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "66442b03-76f6-447e-8499-62ad5648f83f", - "name": "examiner", - "scopeParamRequired": false, - "composite": true, - "composites": { - "realm": [ - "editor" - ] - }, - "clientRole": false, - "containerId": "registry" - }, - { - "id": "f6a090ad-64c2-4d09-9679-e94501116139", - "name": "internal_user", - "scopeParamRequired": false, - "composite": false, - "clientRole": false, - "containerId": "registry" - } - ], - "client": { - "vue-client": [], - "realm-management": [ - { - "id": "eb67f37a-8a5a-4155-a5b4-62bc4d3d8b93", - "name": "view-authorization", - "description": "${role_view-authorization}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "5bf9ae81-d05c-49b7-9fa2-0584b539623b", - "name": "impersonation", - "description": "${role_impersonation}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "146b6482-7fca-40be-be5d-a88d861e752d", - "name": "view-users", - "description": "${role_view-users}", - "scopeParamRequired": false, - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-groups", - "query-users" - ] - } - }, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "cdc69084-1d9c-4e7e-a6c0-909c32a0a806", - "name": "query-users", - "description": "${role_query-users}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "0b3f321e-7f93-4ffe-8808-439a07dfa12b", - "name": "view-clients", - "description": "${role_view-clients}", - "scopeParamRequired": false, - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-clients" - ] - } - }, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "20bf7354-2d48-4720-ab99-d0c2f8f6c774", - "name": "create-client", - "description": "${role_create-client}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "a6e91bad-ad24-4569-9861-07a37cdf6d6d", - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "e98ee2d5-6505-4eca-9a04-27edd759046b", - "name": "query-realms", - "description": "${role_query-realms}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "bffb0879-9df8-4826-9a45-367e03dd8bf7", - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "fa8e7bc5-3041-40c7-aea5-e5e45513c4e8", - "name": "view-realm", - "description": "${role_view-realm}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "d4df9071-e65c-4e60-a186-ca5cabef7ed4", - "name": "manage-users", - "description": "${role_manage-users}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "e7a8ffd8-e19b-4655-a32f-d4a1895c18cc", - "name": "realm-admin", - "description": "${role_realm-admin}", - "scopeParamRequired": false, - "composite": true, - "composites": { - "client": { - "realm-management": [ - "view-authorization", - "view-users", - "impersonation", - "query-users", - "view-clients", - "create-client", - "manage-authorization", - "query-realms", - "view-identity-providers", - "view-realm", - "manage-users", - "query-groups", - "manage-events", - "manage-realm", - "manage-identity-providers", - "manage-clients", - "query-clients", - "view-events" - ] - } - }, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "80ac5c0d-e15f-4c61-b9b6-173571c7a0d0", - "name": "query-groups", - "description": "${role_query-groups}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "076bbf68-63b0-4874-b8ed-f87694664dad", - "name": "manage-events", - "description": "${role_manage-events}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "e80e68b5-4321-41e5-ad64-982003dca137", - "name": "manage-realm", - "description": "${role_manage-realm}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "5d4a49b3-ed1b-425d-89b2-dfa593d8b761", - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "f6fb4e61-0a27-4270-893e-ea82eba78890", - "name": "manage-clients", - "description": "${role_manage-clients}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "b888d931-6523-46c7-b68d-b598a5fb0039", - "name": "view-events", - "description": "${role_view-events}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - }, - { - "id": "3a43c9b3-b279-40d4-9593-dac07cbd0cda", - "name": "query-clients", - "description": "${role_query-clients}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "b52f66a3-0938-4359-820a-888c3b238c71" - } - ], - "namex-api-service": [], - "security-admin-console": [], - "admin-cli": [], - "broker": [ - { - "id": "6804dda4-8449-47fd-9bdd-8d098c41140e", - "name": "read-token", - "description": "${role_read-token}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "7bebf0ed-21cf-46f1-8391-b427039e3b0f" - } - ], - "account": [ - { - "id": "b7b0af88-e9f0-4fb2-a833-df0938215110", - "name": "manage-account", - "description": "${role_manage-account}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "5b221d6b-38be-44c0-b08b-d6d169b6c71f" - }, - { - "id": "cee71bd1-f37a-471a-9fc4-36adea61e4f4", - "name": "view-profile", - "description": "${role_view-profile}", - "scopeParamRequired": false, - "composite": false, - "clientRole": true, - "containerId": "5b221d6b-38be-44c0-b08b-d6d169b6c71f" - } - ] - } - }, - "groups": [], - "defaultRoles": [ - "uma_authorization", - "offline_access" - ], - "requiredCredentials": [ - "password" - ], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "users": [ - { - "id": "6d99da63-b4b4-4fce-9041-a48f08b88b62", - "createdTimestamp": 1457018495663, - "username": "admin", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "omid.x.zamani@gov.bc.ca", - "credentials": [ - { - "type": "password", - "hashedSaltedValue": "xR6Zy+yIkDrK3lcXV2DzXcdp6PHCyfFKhE6aYq9/Ya1nR5jQuhgvnp57a5OyoVYNKfa43xL6fantyTivxEKAuw==", - "salt": "5ThvuQW+u/oZU8ovDvrepg==", - "hashIterations": 20000, - "counter": 0, - "algorithm": "pbkdf2", - "digits": 0, - "period": 0, - "createdDate": 1457018506000, - "config": {} - } - ], - "disableableCredentialTypes": ["password"], - "requiredActions": [], - "realmRoles": ["ADMIN", "offline_access"], - "clientRoles": { - "account": ["manage-account", "view-profile"] - }, - "groups": [] - }, - { - "id": "266c7fe3-9092-4314-91b4-71429e961ff8", - "createdTimestamp": 1524804987658, - "username": "postman", - "enabled": true, - "totp": false, - "emailVerified": false, - "credentials": [ - { - "type": "password", - "hashedSaltedValue": "KC4fNReIuhWwWS6j9zqoAsASC8scUoU5TKmg1gBty4+Yg4beJsZJgnjWZm9rRyFkizlbgPEjHoRr5ciIekK3tQ==", - "salt": "oGmFubuAIYBlSJZjI4UGUw==", - "hashIterations": 20000, - "counter": 0, - "algorithm": "pbkdf2", - "digits": 0, - "period": 0, - "createdDate": 1524811567737, - "config": {} - } - ], - "disableableCredentialTypes": ["password"], - "requiredActions": [], - "realmRoles": ["uma_authorization", "offline_access", "examiner"], - "clientRoles": { - "account": ["manage-account", "view-profile"] - }, - "groups": [] - }, - { - "id": "898eea86-3383-4677-8a49-63b5b0698f7c", - "createdTimestamp": 1457018474914, - "username": "user", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "user@email.com", - "credentials": [ - { - "type": "password", - "hashedSaltedValue": "eCFRd74EtbwV9GqJUMru7nPYIjYA81Zz+vk0rx8ScAAKqhAoNY337yZlstFBbXfyM8+pLeNOcd4j/JbY4255Wg==", - "salt": "tpujc7R9RR4hViotAF2HUw==", - "hashIterations": 20000, - "counter": 0, - "algorithm": "pbkdf2", - "digits": 0, - "period": 0, - "createdDate": 1457018481000, - "config": {} - } - ], - "disableableCredentialTypes": ["password"], - "requiredActions": [], - "realmRoles": ["USER", "offline_access"], - "clientRoles": { - "account": ["manage-account", "view-profile"] - }, - "groups": [] - } - ], - "otpSupportedApplications": [ - "FreeOTP", - "Google Authenticator" - ], - "clients": [ - { - "id": "5b221d6b-38be-44c0-b08b-d6d169b6c71f", - "clientId": "account", - "name": "${client_account}", - "baseUrl": "/*", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "5abdcb03-9dc6-4789-8c1f-8230c7d7cb79", - "defaultRoles": [ - "manage-account", - "view-profile" - ], - "redirectUris": [ - "http://localhost:8080/*" - ], - "webOrigins": [ - "*" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "71e4be8a-7e18-466d-a34f-7086053f11bf", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "92ffbf59-f10a-47c4-a921-2da1f7ea4c0c", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "ab738104-0faa-4f97-a434-4f4ca579f0e6", - "name": "display_name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "false", - "user.attribute": "displayName", - "id.token.claim": "false", - "access.token.claim": "true", - "claim.name": "display_name", - "jsonType.label": "String" - } - }, - { - "id": "24ed7c89-f8c7-4e92-9226-d0ddbbd39427", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "6df21f3c-89a8-4370-9f25-e1bd473ddd2c", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "841e1294-340a-427f-b4df-4a61d387cc8a", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "7951f220-b7df-4506-b3d2-99f2a7c40c8d", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "42f591c5-7157-4905-a0a4-99a6c65b7c80", - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "42379daf-ed81-489f-b670-10c5abf4381a", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "882043fc-43f9-4f1a-8b92-103dbb9b38bf", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "e0fdc950-b204-4b0d-888c-1e1790dc8fdc", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "01ec14ad-2c5d-4f3b-bc9d-b0cff56c1014", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "b7812233-47d1-4816-bb58-a8c54fb30fd0", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "b590024a-be73-45f5-b3bf-805e2c81cefc", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "b771e546-e492-4595-a0e3-156332bc1e07", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "af11ca0c-3c29-420d-b548-77c4e0310946", - "clientId": "security-admin-console", - "name": "${client_security-admin-console}", - "baseUrl": "/auth/admin/registry/console/index.html", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "d6e4b70a-73b1-49f3-be1b-d8c8bcaeaf6d", - "redirectUris": [ - "/auth/admin/registry/console/*" - ], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "cc998d54-0c0d-4682-91a1-a26887585410", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "99b4648d-a000-4a0c-a124-83892efd627e", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "d16456e4-9cdb-49b8-b353-aa83e4ab5a23", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "3ecc12b9-a10b-4c9b-80fe-6f18923e92ee", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "b13c6c52-14ae-4d91-b1eb-7169bf1f50b8", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "e55c919f-9a38-44a6-a861-89dff16de470", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "e03f4879-8ae8-400e-a3a9-5f85a2291057", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "consentText": "${locale}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "279a070f-ff25-4029-b83d-1579d45804fd", - "clientId": "namex-api-service", - "name": "namex-api-service", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "ce43801d-584d-470e-9593-ce1674dda4ac", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.assertion.signature": "false", - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "saml.encrypt": "false", - "saml_force_name_id_format": "false", - "saml.client.signature": "false", - "saml.authnstatement": "false", - "saml.server.signature": "false", - "saml.server.signature.keyinfo.ext": "false", - "saml.onetimeuse.condition": "false" - }, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "3c485ce3-6f76-44aa-8969-479c05be1af1", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "971fee75-27cb-425e-9438-2720ce40914a", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "788accd0-7295-4c67-8376-4e4791baa52f", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "95e10207-1f1f-4592-a65b-1e6ef2e0525f", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "d224a23e-d578-4491-a073-3a23555ff45e", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "eb5f71c2-737b-4e95-8e0f-81c6779c8082", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "7bebf0ed-21cf-46f1-8391-b427039e3b0f", - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "09d87bae-9e07-41be-8bf7-8ea52ce5f41c", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "e380413b-6132-451c-8ada-b01cb1f4f080", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "58ad1a0d-63ba-44b0-aad5-3db59c7ee294", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "1f11568d-e26d-4a7e-8482-1bea33ba0828", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "af585617-b189-46b8-baf3-f4daa00f8f91", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "34786671-894d-4469-908f-e05534295dfb", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "799c4eb4-5dc4-4c71-8b7a-77773342ddbf", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "703e065e-a1e2-4185-ad7c-1b1936d8b5f9", - "clientId": "vue-client", - "name": "vue-client", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "ec6b9969-f314-4c41-b8ea-a51616df1e93", - "redirectUris": [ - "http://localhost/*", - "http://registry.wolpert.ca:8083/*" - ], - "webOrigins": [ - "*" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.assertion.signature": "false", - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "saml.encrypt": "false", - "saml_force_name_id_format": "false", - "saml.client.signature": "false", - "saml.authnstatement": "false", - "saml.server.signature": "false", - "saml.server.signature.keyinfo.ext": "false", - "saml.onetimeuse.condition": "false" - }, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "6b417f92-5a31-4f32-a061-44f85f52836c", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "2aedb708-5345-4cf3-b2c2-a37f604e966b", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "532c0a1b-e839-4ffd-8144-07b0d521954f", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "8c95a355-eaf4-4ac7-b7c0-6b015bfd7099", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - }, - { - "id": "651370bb-78a1-469b-bc9c-842cb25def2e", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "1de9ac9f-de7b-4377-8da4-f3f50c47126f", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - }, - { - "id": "b52f66a3-0938-4359-820a-888c3b238c71", - "clientId": "realm-management", - "name": "${client_realm-management}", - "surrogateAuthRequired": false, - "enabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "3c486358-220b-4752-baae-6f5ec62310f2", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "3f508c27-62cb-4bb4-b6e5-873b1e7d5cd2", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${familyName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "7319c52b-9c36-471c-ad32-51449ca83eae", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${username}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "6b7fdd43-6d79-4923-8beb-b4a9531460fd", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${givenName}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "08e2628d-de01-403e-aa8f-d1abcfb4e894", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": true, - "consentText": "${fullName}", - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "b54d6668-57e9-4757-af84-36ea030f16cb", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": true, - "consentText": "${email}", - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "8d4847c8-f8bb-4f26-b52b-b6203be15169", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - } - ], - "useTemplateConfig": false, - "useTemplateScope": false, - "useTemplateMappers": false - } - ], - "clientTemplates": [], - "browserSecurityHeaders": { - "xContentTypeOptions": "nosniff", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "xXSSProtection": "1; mode=block", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "eventsEnabled": false, - "eventsListeners": [ - "jboss-logging" - ], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "id": "9f3fc2dc-e93a-4812-a176-8e3f49deccc9", - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "6c7a10d3-e13e-45b0-9a91-2551cdcdc02f", - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": [ - "200" - ] - } - }, - { - "id": "5be2dea8-f688-4aec-a450-f6c4edd00401", - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": [ - "true" - ], - "client-uris-must-match": [ - "true" - ] - } - }, - { - "id": "24e86563-cf93-4f3e-ba62-55237ea94eef", - "name": "Allowed Client Templates", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": {} - }, - { - "id": "0dae4b3a-9b5d-4c28-9818-cb7200f928ee", - "name": "Allowed Client Templates", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "8bd9f481-7f07-4356-a287-57c8f909b386", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-usermodel-attribute-mapper", - "saml-role-list-mapper", - "oidc-usermodel-property-mapper", - "saml-user-attribute-mapper", - "oidc-address-mapper", - "saml-user-property-mapper", - "oidc-full-name-mapper", - "oidc-sha256-pairwise-sub-mapper" - ], - "consent-required-for-all-mappers": [ - "true" - ] - } - }, - { - "id": "0c7fa637-322e-4c37-b73a-d37fc4a0fdbd", - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "442a5d32-184b-4d77-8383-6d341576c93e", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "saml-user-property-mapper", - "oidc-address-mapper", - "saml-user-attribute-mapper", - "oidc-usermodel-property-mapper", - "oidc-full-name-mapper", - "saml-role-list-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-attribute-mapper" - ], - "consent-required-for-all-mappers": [ - "true" - ] - } - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "id": "af95669f-e356-4cf8-9e0a-0c2c754c39ee", - "name": "rsa", - "providerId": "rsa", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEpAIBAAKCAQEA0Q2QN4cCxc10Azb7CC4uas7efiPZOwwUHykxLIfHtc8Oxw2PkoBrSeMyA/skCewDvMJZHmc2SOdnA6DpqZ6rlXe7R6yzM4ytHIg4ijE4+PYotiP0lUTTTgIf3mbvaAiLUTZV1sz4TjmcSk95v4mb4CsqaDMGE+2EPyp0DW0NBqaynaE4aVKuGOxJAosJBBZodAOlthpMU59hL7JqBXQJKNvQsyxnJYHgJnLWaGUn8D8+Y1MMJgVjSrmtxqv/2coVaSJrRqSYirn+GmYgRGRR/BUyIikUROyGLo5Y/LtUvuTQM1vetHkcd3LJP7MpWqG9PDzvx412FbPw0sISzCLgaQIDAQABAoIBAH3DTRk6jX51pO8yKj63mZEDtnHoGw0crRY57608WfaUA+3XyKF3xBOFLFecxAd8BpuNx5M0wEGHhD94lFlIbSzUBMSPrpa6cIITqP/psL6FR+SbKM6LwxrQyF9BSZEqYrWOnIFGebKFkfJacYG6xR1Mmoxo9aUTZFWjsoFJM2bl1uIpTzNDX08qhoGaCUIK8IvCtBFxiwJU3d7MCtkvC7Mdw/aVJ5peVsm45eEWrBlcAdKVm/uJupZCLHv0iJW0+iekp6Mo/xfMZaYPOey/pLzXE9aKKWpeIziHUYNvj2uIynHgSk2v7VF56YGxJGOdQyu4plBuMd8QezCarTBo1qkCgYEA92Kt6eO4TsnRgyyz3w38squsOCEmFhqp1tFkTIbXbcUPXHjiROI7oeHRRPRQuFK2Ql9KkdfJ5nf1xJTmNXTJnWZsB8xZn/WvNo2cs00H9Vom7lXz1sMypq6SFcrX5ZlTZrDL5VYky+22MyjjSqQV088Qbq7khsoOdzHqYUd2kqcCgYEA2FUrKklHFkvVRZ7GWp7lUb6AsyYXi3PnrsjH4/uodTInwQoq+nZ0OGyAU2D12G2TZ6E8k0itFBNQxOI80ezQ4z91FbrHauitcz1Uo7Qyux48v/0aAhc6VjQxi5NvV/WBD/2hGhgfpf37hlbX7xjRCHhvJmfVmxjGy+PJv1d3pm8CgYEAkSWAzN4jYXbgSoqL7q+SOHKpVTV91zJFvBiXNJ9Y/rrETIlQmQHz4wNPeZmvUraU4VRvuCpX97UiqfiDKZIgSQ2zDNBFNak86FEmQ71KkhYXCXe83aEhstJyO/8LtVbErFfHCgQFCvGqGVJAKackd2XiSk6rHrqmyiCrV/BKDbkCgYATyf6v2JIrJPa2bqbAZWaeyqllOefPduPLpkw6u6uWROdEDlUOIkXRX4OBnQ4AJU3lnec3pd9HRdkOmY9suJgHIafpO/PAANbVAtg6PWKioUp3WYTGii9o8U9S8EKwLiIHP3E8PNawTYJzsIACPCfq+XFZdwggLu0Ie6bZ3Ptx+wKBgQCEHoDjdbs7zMCyGXTm1/TeNvQ5Q1R1P9vUqsHFZdmMOyfqexPsBfinhwWeA0QQd78HPnaB0iO663cE4QvYo4Qj28Dl/N7LGwpMUlB5PC0GxYkH34OaAspXc2WU6AhgMnY2oWXrJxDIh7zzlrtcLSZrFmNrkigvgDEnFfPeJvXAWA==" - ], - "certificate": [ - "MIICmzCCAYMCBgFTPQ+6VzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZncmFpbHMwHhcNMTYwMzAzMTUxNzM0WhcNMjYwMzAzMTUxOTE0WjARMQ8wDQYDVQQDDAZncmFpbHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRDZA3hwLFzXQDNvsILi5qzt5+I9k7DBQfKTEsh8e1zw7HDY+SgGtJ4zID+yQJ7AO8wlkeZzZI52cDoOmpnquVd7tHrLMzjK0ciDiKMTj49ii2I/SVRNNOAh/eZu9oCItRNlXWzPhOOZxKT3m/iZvgKypoMwYT7YQ/KnQNbQ0GprKdoThpUq4Y7EkCiwkEFmh0A6W2GkxTn2EvsmoFdAko29CzLGclgeAmctZoZSfwPz5jUwwmBWNKua3Gq//ZyhVpImtGpJiKuf4aZiBEZFH8FTIiKRRE7IYujlj8u1S+5NAzW960eRx3csk/sylaob08PO/HjXYVs/DSwhLMIuBpAgMBAAEwDQYJKoZIhvcNAQELBQADggEBALj8TqkAJFynu8fxmV3CakTPQM6SMTOyviIM0Kns+aT2qeQTrrf2Iglj0naRU3cfv2lbiRDUjIIrA6wKTI7RL8kfXwlIB/eN1Tn/QdGUzW9/Zkzq2sr+YdnyJLWf7zDIDJbvQaStOjR9aIBXZJ4JoYVOatV3Y/OZSDampU7/sYwHdCe1UtdxQzxcOkguRfdJC5/jSTgxjx/OUoN/4i60hJrfhGh/HLrVC180niyg0gOIZjY+VUQzYiXnR1gJiSRrpY3oJdHhHPNQs5lbXoez3jppu78wmQqZWqjeygBT5uBdwJUpuPSNy9FQGpeTZm+UV0QI8w944C7xZtZIVS53DqQ=" - ], - "priority": [ - "100" - ] - } - }, - { - "id": "8fcc06c9-e7b7-4e1a-ba17-d618b79f5e38", - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - }, - { - "id": "d570fc8e-4deb-4371-bb73-3184c588114e", - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "priority": [ - "100" - ] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "id": "9efee42f-0a19-45b4-b959-fdaee96f0f3b", - "alias": "Handle Existing Account", - "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "idp-email-verification", - "requirement": "ALTERNATIVE", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 30, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "id": "d1b25912-b6a9-4c8f-b2d9-0e834f7d6bf1", - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-otp-form", - "requirement": "OPTIONAL", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "40d90ee1-11d8-438b-807e-f234850cab40", - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-spnego", - "requirement": "DISABLED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "identity-provider-redirector", - "requirement": "ALTERNATIVE", - "priority": 25, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 30, - "flowAlias": "forms", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "id": "2393b56b-7b2f-4aff-9ff9-715cc8d16c4c", - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "requirement": "ALTERNATIVE", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "client-jwt", - "requirement": "ALTERNATIVE", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "9c40bbb3-50a7-4391-a2f8-811b3903a954", - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "direct-grant-validate-password", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "direct-grant-validate-otp", - "requirement": "OPTIONAL", - "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "80468827-0db3-4af3-8eff-f34623312a06", - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "7fba12e2-32be-4da4-8247-2cf0ea8bcc19", - "alias": "first broker login", - "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "requirement": "ALTERNATIVE", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "requirement": "ALTERNATIVE", - "priority": 30, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "id": "028360b1-9331-4450-9f17-ab1171ad6579", - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "auth-otp-form", - "requirement": "OPTIONAL", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "af6718e6-1f12-4f47-96a6-72a97b6c770f", - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "requirement": "REQUIRED", - "priority": 10, - "flowAlias": "registration form", - "userSetupAllowed": false, - "autheticatorFlow": true - } - ] - }, - { - "id": "75ee3b3e-b753-41a1-b90e-997fd81edc8d", - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-profile-action", - "requirement": "REQUIRED", - "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-password-action", - "requirement": "REQUIRED", - "priority": 50, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "registration-recaptcha-action", - "requirement": "DISABLED", - "priority": 60, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "f839aa65-9f81-4124-a248-19307c97d0a7", - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-credential-email", - "requirement": "REQUIRED", - "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-password", - "requirement": "REQUIRED", - "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false - }, - { - "authenticator": "reset-otp", - "requirement": "OPTIONAL", - "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - }, - { - "id": "21bc71a9-cc19-42cf-9171-fc89d4c517d2", - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "requirement": "REQUIRED", - "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false - } - ] - } - ], - "authenticatorConfig": [ - { - "id": "95a9cfba-ee8d-4544-9855-4504f38840cc", - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "id": "03b79da1-4335-4cc7-9dea-b90c8875e370", - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "config": {} - }, - { - "alias": "terms_and_conditions", - "name": "Terms and Conditions", - "providerId": "terms_and_conditions", - "enabled": false, - "defaultAction": false, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "_browser_header.xXSSProtection": "1; mode=block", - "_browser_header.xFrameOptions": "SAMEORIGIN", - "_browser_header.strictTransportSecurity": "max-age=31536000; includeSubDomains", - "permanentLockout": "false", - "quickLoginCheckMilliSeconds": "1000", - "_browser_header.xRobotsTag": "none", - "maxFailureWaitSeconds": "900", - "minimumQuickLoginWaitSeconds": "60", - "failureFactor": "30", - "actionTokenGeneratedByUserLifespan": "300", - "maxDeltaTimeSeconds": "43200", - "_browser_header.xContentTypeOptions": "nosniff", - "actionTokenGeneratedByAdminLifespan": "43200", - "bruteForceProtected": "false", - "_browser_header.contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "waitIncrementSeconds": "60" - }, - "keycloakVersion": "3.4.3.Final" -} \ No newline at end of file diff --git a/keycloak-local-testserver/setup.sh b/keycloak-local-testserver/setup.sh deleted file mode 100755 index fb212e02a..000000000 --- a/keycloak-local-testserver/setup.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -KEYCLOAK_URI=http://localhost:8080/auth -REALM_JSON=/opt/keycloak/registry-realm.json -JBOSS_CLI=$KEYCLOAK_HOME/bin/jboss-cli.sh - -echo -e "Starting Keycloak in background..." -$KEYCLOAK_HOME/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 & -until `$JBOSS_CLI -c "ls /deployment" &> /dev/null`; do - sleep 1 -done - -ACCESS_TOKEN=$(curl -s -X POST $KEYCLOAK_URI/realms/master/protocol/openid-connect/token \ - -d grant_type=password \ - -d client_id=admin-cli \ - -d username=admin -d password=admin | jq -r '.access_token') - -# Remove existing realm -curl -s -S -X DELETE -H "Authorization: Bearer $ACCESS_TOKEN" $KEYCLOAK_URI/admin/realms/registry - -echo "Importing realm..." -curl -s -S -H "Content-Type: application/json" -H "Authorization: Bearer $ACCESS_TOKEN" -d @$REALM_JSON $KEYCLOAK_URI/admin/realms - -# HOW TO EXPORT A REALM -# The Keycloak admin UI does not (yet) support exporting realms. -# Assuming you changed something in Keycloak admin which you would like to keep, -# first attach to the running keycloak instance like this: -# -# docker exec -it keycloak /bin/bash -# -# Next, without killing keycloak (which would stop the container), we'll run a second instance on a different port. -# This is a round-about way to start the export process, but it works. -# -# cd /opt/keycloak -# keycloak-3.0.0.Final/bin/standalone.sh \ -# -Dkeycloak.migration.action=export \ -# -Dkeycloak.migration.provider=singleFile \ -# -Dkeycloak.migration.realmName=registry \ -# -Dkeycloak.migration.file=registry-realm.json \ -# -Djboss.socket.binding.port-offset=100 - -echo "Shutting down..." -$JBOSS_CLI -c ':shutdown' &> /dev/null diff --git a/keycloak-local-testserver/startup.sh b/keycloak-local-testserver/startup.sh deleted file mode 100755 index 338ec2b60..000000000 --- a/keycloak-local-testserver/startup.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -$KEYCLOAK_HOME/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 diff --git a/keycloak-local/servicebc-local-realm.json b/keycloak-local/servicebc-local-realm.json new file mode 100644 index 000000000..39094e375 --- /dev/null +++ b/keycloak-local/servicebc-local-realm.json @@ -0,0 +1,555 @@ +{ + "realm": "servicebc-local", + "displayName": "ServiceBC Local Dev", + "enabled": true, + "registrationAllowed": false, + "resetPasswordAllowed": false, + "rememberMe": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": true, + "roles": { + "realm": [ + { + "name": "internal_user", + "description": "Local ServiceBC internal user" + }, + { + "name": "online_appointment_user", + "description": "Local ServiceBC appointment user" + } + ] + }, + "clients": [ + { + "clientId": "theq-frontend", + "name": "TheQ Frontend", + "enabled": true, + "protocol": "openid-connect", + "publicClient": true, + "standardFlowEnabled": true, + "directAccessGrantsEnabled": false, + "frontchannelLogout": true, + "rootUrl": "http://localhost:8080", + "baseUrl": "/", + "redirectUris": [ + "http://localhost:8080/*" + ], + "webOrigins": [ + "http://localhost:8080" + ], + "attributes": { + "post.logout.redirect.uris": "http://localhost:8080/*", + "pkce.code.challenge.method": "S256" + }, + "protocolMappers": [ + { + "name": "audience-theq-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-queue-management-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "theq_username", + "claim.name": "username", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "identity_provider", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "identity_provider", + "claim.name": "identity_provider", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "display_name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "display_name", + "claim.name": "display_name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "userid", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "userid", + "claim.name": "userid", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "name", + "claim.name": "name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "firstname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "firstName", + "claim.name": "firstname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "lastname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "lastName", + "claim.name": "lastname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "clientId": "theq-appointment-frontend", + "name": "TheQ Appointment Frontend", + "enabled": true, + "protocol": "openid-connect", + "publicClient": true, + "standardFlowEnabled": true, + "directAccessGrantsEnabled": false, + "frontchannelLogout": true, + "rootUrl": "http://localhost:8081", + "baseUrl": "/", + "redirectUris": [ + "http://localhost:8081/*" + ], + "webOrigins": [ + "http://localhost:8081" + ], + "attributes": { + "post.logout.redirect.uris": "http://localhost:8081/*", + "pkce.code.challenge.method": "S256" + }, + "protocolMappers": [ + { + "name": "audience-theq-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-queue-management-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "theq_username", + "claim.name": "username", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "identity_provider", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "identity_provider", + "claim.name": "identity_provider", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "display_name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "display_name", + "claim.name": "display_name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "userid", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "userid", + "claim.name": "userid", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "name", + "claim.name": "name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "firstname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "firstName", + "claim.name": "firstname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "lastname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "lastName", + "claim.name": "lastname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "clientId": "theq-queue-management-api", + "name": "TheQ Queue Management API", + "enabled": true, + "protocol": "openid-connect", + "publicClient": false, + "secret": "theq-local-dev-secret", + "standardFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "frontchannelLogout": true, + "protocolMappers": [ + { + "name": "audience-theq-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-queue-management-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "theq_username", + "claim.name": "username", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "identity_provider", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "identity_provider", + "claim.name": "identity_provider", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "display_name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "display_name", + "claim.name": "display_name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "userid", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "userid", + "claim.name": "userid", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "user.attribute": "name", + "claim.name": "name", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "firstname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "firstName", + "claim.name": "firstname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "lastname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "user.attribute": "lastName", + "claim.name": "lastname", + "jsonType.label": "String", + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + } + ], + "users": [ + { + "username": "democsr@idir", + "enabled": true, + "emailVerified": true, + "firstName": "CSR", + "lastName": "Demo CITZ:EX", + "email": "csrdemo@example.com", + "attributes": { + "theq_username": [ + "democsr" + ], + "identity_provider": [ + "idir" + ], + "display_name": [ + "CSR, Demo CITZ:EX" + ], + "userid": [ + "CSRDEMO" + ], + "name": [ + "CSR, Demo CITZ:EX" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "internal_user" + ] + }, + { + "username": "demoga@idir", + "enabled": true, + "emailVerified": true, + "firstName": "GA", + "lastName": "Demo CITZ:EX", + "email": "gademo@example.com", + "attributes": { + "theq_username": [ + "demoga" + ], + "identity_provider": [ + "idir" + ], + "display_name": [ + "GA, Demo CITZ:EX" + ], + "userid": [ + "DEMOGA" + ], + "name": [ + "GA, Demo CITZ:EX" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "internal_user" + ] + }, + { + "username": "admin@idir", + "enabled": true, + "emailVerified": true, + "firstName": "Admin", + "lastName": "Test CITZ:EX", + "email": "admin@example.com", + "attributes": { + "theq_username": [ + "admin" + ], + "identity_provider": [ + "idir" + ], + "display_name": [ + "Admin, Test CITZ:EX" + ], + "userid": [ + "ADMIN" + ], + "name": [ + "Admin, Test CITZ:EX" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "internal_user" + ] + }, + { + "username": "citizen@bceidboth", + "enabled": true, + "emailVerified": true, + "firstName": "Test", + "lastName": "Citizen", + "email": "citizen@example.com", + "attributes": { + "theq_username": [ + "citizen" + ], + "identity_provider": [ + "bceid" + ], + "display_name": [ + "Test Citizen" + ], + "userid": [ + "CITIZEN" + ], + "name": [ + "Test Citizen" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "online_appointment_user" + ] + } + ] +} From ecb570c4597b44b0174232411725bb98b85db3ec Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 31 Mar 2026 11:58:40 -0700 Subject: [PATCH 27/81] Update requirements.txt --- api/requirements.txt | 474 +++++++++++++++++++++++++-------------- api/requirements_dev.txt | 444 ++++++++++++++++++++---------------- 2 files changed, 563 insertions(+), 355 deletions(-) diff --git a/api/requirements.txt b/api/requirements.txt index b4f0c9470..01a9961fd 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --no-dev --format requirements.txt --no-emit-project --output-file requirements.txt +# uv export --frozen --format requirements-txt --no-emit-project --output-file requirements.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc @@ -24,51 +24,35 @@ argon2-cffi-bindings==25.1.0 \ --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ --hash=sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44 \ + --hash=sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a \ + --hash=sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f \ --hash=sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2 \ --hash=sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0 \ + --hash=sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f \ + --hash=sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623 \ + --hash=sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b \ + --hash=sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44 \ --hash=sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98 \ --hash=sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500 \ --hash=sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94 \ + --hash=sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6 \ --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ + --hash=sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85 \ + --hash=sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92 \ --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ - --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a + --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a \ + --hash=sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb # via argon2-cffi -async-timeout==5.0.1 ; python_full_version < '3.11.3' \ - --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ - --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 - # via redis +astroid==4.0.4 \ + --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ + --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 + # via pylint attrs==26.1.0 \ --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # jsonschema # referencing -backports-zstd==1.3.0 \ - --hash=sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e \ - --hash=sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f \ - --hash=sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5 \ - --hash=sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d \ - --hash=sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9 \ - --hash=sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a \ - --hash=sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120 \ - --hash=sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5 \ - --hash=sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b \ - --hash=sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3 \ - --hash=sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9 \ - --hash=sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c \ - --hash=sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72 \ - --hash=sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3 \ - --hash=sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c \ - --hash=sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f \ - --hash=sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957 \ - --hash=sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533 \ - --hash=sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2 \ - --hash=sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4 \ - --hash=sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a \ - --hash=sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943 \ - --hash=sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2 \ - --hash=sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1 - # via flask-compress bidict==0.23.1 \ --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 @@ -80,29 +64,30 @@ blinker==1.9.0 \ # flask # flask-socketio brotli==1.2.0 ; platform_python_implementation != 'PyPy' \ - --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ - --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ - --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ - --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ - --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ - --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ - --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ - --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ - --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ - --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ - --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a + --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \ + --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \ + --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \ + --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \ + --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \ + --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \ + --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \ + --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \ + --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \ + --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 # via flask-compress brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ - --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ + --hash=sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd \ + --hash=sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec \ + --hash=sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5 \ + --hash=sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000 \ --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \ --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \ - --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \ --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \ --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \ - --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \ - --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \ - --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1 + --hash=sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac \ + --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede # via flask-compress cachelib==0.13.0 \ --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ @@ -117,44 +102,69 @@ certifi==2026.2.25 \ # minio # requests cffi==2.0.0 \ - --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ - --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ - --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ - --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ - --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ - --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ - --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ - --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ - --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ - --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ - --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ - --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ - --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 # via # argon2-cffi-bindings # brotlicffi # cryptography # gevent charset-normalizer==3.4.6 \ - --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ + --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ + --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ + --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ + --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ - --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ - --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ - --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ - --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ - --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ + --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ + --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ + --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ + --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ + --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ + --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ + --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ + --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ + --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ + --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ + --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ + --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ + --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ + --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ + --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ - --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ - --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ - --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ - --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ - --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ - --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ - --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ - --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ - --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ - --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed + --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ + --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ + --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ + --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ + --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ + --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ + --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ + --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ + --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ + --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ + --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ + --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ + --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 # via requests click==8.3.1 \ --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ @@ -166,44 +176,93 @@ click==8.3.1 \ colorama==0.4.6 ; sys_platform == 'win32' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via click + # via + # click + # pylint + # pytest +coverage==7.13.5 \ + --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \ + --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \ + --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \ + --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \ + --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \ + --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \ + --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \ + --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ + --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \ + --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \ + --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \ + --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \ + --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \ + --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \ + --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \ + --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \ + --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \ + --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \ + --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \ + --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \ + --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \ + --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \ + --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \ + --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ + --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \ + --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \ + --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \ + --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \ + --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \ + --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \ + --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \ + --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f + # via pytest-cov cryptography==46.0.6 \ --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ + --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ + --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ + --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ + --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ + --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ + --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ + --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ + --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 # via flask-jwt-oidc +dill==0.4.1 \ + --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ + --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa + # via pylint ecdsa==0.19.2 \ --hash=sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930 \ --hash=sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399 @@ -275,26 +334,35 @@ flask-sqlalchemy==3.1.1 \ # flask-migrate # queue-api gevent==25.9.1 \ - --hash=sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7 \ - --hash=sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457 \ - --hash=sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff \ - --hash=sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586 \ - --hash=sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235 \ + --hash=sha256:0adb937f13e5fb90cca2edf66d8d7e99d62a299687400ce2edee3f3504009356 \ + --hash=sha256:1a3fe4ea1c312dbf6b375b416925036fe79a40054e6bf6248ee46526ea628be1 \ + --hash=sha256:1d0f5d8d73f97e24ea8d24d8be0f51e0cf7c54b8021c1fddb580bf239474690f \ + --hash=sha256:427f869a2050a4202d93cf7fd6ab5cffb06d3e9113c10c967b6e2a0d45237cb8 \ --hash=sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd \ - --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ - --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 + --hash=sha256:b5a67a0974ad9f24721034d1e008856111e0535f1541499f72a733a73d658d1c \ + --hash=sha256:bb63c0d6cb9950cc94036a4995b9cc4667b8915366613449236970f4394f94d7 \ + --hash=sha256:c049880175e8c93124188f9d926af0a62826a3b81aa6d3074928345f8238279e \ + --hash=sha256:ddd3ff26e5c4240d3fbf5516c2d9d5f2a998ef87cfb73e1429cfaeaaec860fa6 # via queue-api greenlet==3.3.2 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or platform_python_implementation == 'CPython' \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ + --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ + --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ + --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 + --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ + --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ + --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ + --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ + --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ + --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ + --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ + --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ + --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ + --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ + --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ + --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ + --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ + --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 # via # gevent # sqlalchemy @@ -314,6 +382,14 @@ importlib-resources==6.5.2 \ --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec # via flask-restx +iniconfig==2.3.0 \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + # via pytest +isort==8.0.1 \ + --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ + --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 + # via pylint itsdangerous==2.2.0 \ --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 @@ -343,18 +419,29 @@ mako==1.3.10 \ --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 # via alembic markupsafe==3.0.3 \ - --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ - --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ - --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ - --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ - --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ - --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ - --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ - --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ - --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ - --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ - --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via # flask # flask-admin @@ -374,6 +461,10 @@ marshmallow-sqlalchemy==1.4.2 \ --hash=sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c \ --hash=sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40 # via queue-api +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e + # via pylint minio==7.2.20 \ --hash=sha256:95898b7a023fbbfde375985aa77e2cd6a0762268db79cf886f002a9ea8e68598 \ --hash=sha256:eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e @@ -384,19 +475,30 @@ packaging==26.0 \ # via # gunicorn # kombu + # pytest +platformdirs==4.9.4 \ + --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 + # via pylint +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + # via + # pytest + # pytest-cov psycopg2-binary==2.9.11 \ - --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \ - --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \ - --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \ - --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \ - --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \ - --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \ - --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \ + --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \ + --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \ + --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \ + --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \ + --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \ + --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \ --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \ - --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \ - --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \ - --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ - --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 + --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \ + --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \ + --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \ + --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \ + --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747 # via queue-api pyasn1==0.6.3 \ --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ @@ -422,10 +524,24 @@ pycryptodome==3.23.0 \ --hash=sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843 \ --hash=sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa # via minio +pygments==2.19.2 \ + --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ + --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b + # via pytest pyjwt==2.12.1 \ --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b # via flask-jwt-oidc +pylint==4.0.5 \ + --hash=sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2 \ + --hash=sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c +pytest==9.0.2 \ + --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ + --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 + # via pytest-cov +pytest-cov==7.1.0 \ + --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ + --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 @@ -464,34 +580,36 @@ requests==2.33.0 \ # queue-api # snowplow-tracker rpds-py==0.30.0 \ - --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \ - --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \ - --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \ - --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \ - --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \ - --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \ - --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \ - --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \ - --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \ - --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \ - --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \ - --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \ - --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \ - --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \ - --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \ - --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \ - --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \ - --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \ - --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \ - --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \ - --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \ - --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \ - --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \ - --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \ + --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \ + --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \ + --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \ + --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \ + --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \ + --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \ + --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \ + --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \ + --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \ + --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \ + --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \ + --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \ + --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \ + --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \ + --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \ + --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \ + --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \ + --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \ + --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \ + --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \ + --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \ + --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \ + --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \ + --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \ --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ - --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \ - --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \ - --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a + --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \ + --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \ + --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \ + --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \ + --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5 # via # jsonschema # referencing @@ -499,6 +617,25 @@ rsa==4.9.1 \ --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 # via python-jose +ruff==0.15.8 \ + --hash=sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89 \ + --hash=sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1 \ + --hash=sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3 \ + --hash=sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8 \ + --hash=sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762 \ + --hash=sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3 \ + --hash=sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49 \ + --hash=sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb \ + --hash=sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e \ + --hash=sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec \ + --hash=sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34 \ + --hash=sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8 \ + --hash=sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6 \ + --hash=sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7 \ + --hash=sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2 \ + --hash=sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570 \ + --hash=sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a \ + --hash=sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94 simple-websocket==1.1.0 \ --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 @@ -514,27 +651,36 @@ snowplow-tracker==1.1.0 \ --hash=sha256:95d8fdc8bd542fd12a0b9a076852239cbaf0599eda8721deaf5f93f7138fe755 # via queue-api sqlalchemy==2.0.48 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ + --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ + --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ + --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ + --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ + --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ + --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ + --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ + --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ + --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c + --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ + --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ + --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ + --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # queue-api +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via pylint typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 # via # alembic # minio - # referencing # snowplow-tracker # sqlalchemy tzdata==2025.3 \ @@ -578,11 +724,11 @@ zope-event==6.1 \ --hash=sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0 # via gevent zope-interface==8.2 \ - --hash=sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489 \ - --hash=sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c \ - --hash=sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b \ + --hash=sha256:05a0e42d6d830f547e114de2e7cd15750dc6c0c78f8138e6c5035e51ddfff37c \ + --hash=sha256:46c7e4e8cbc698398a67e56ca985d19cb92365b4aafbeb6a712e8c101090f4cb \ + --hash=sha256:561ce42390bee90bae51cf1c012902a8033b2aaefbd0deed81e877562a116d48 \ + --hash=sha256:a87fc7517f825a97ff4a4ca4c8a950593c59e0f8e7bfe1b6f898a38d5ba9f9cf \ + --hash=sha256:aae807efc7bd26302eb2fea05cd6de7d59269ed6ae23a6de1ee47add6de99b8c \ --hash=sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224 \ - --hash=sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322 \ - --hash=sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce \ - --hash=sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466 + --hash=sha256:ccf52f7d44d669203c2096c1a0c2c15d52e36b2e7a9413df50f48392c7d4d080 # via gevent diff --git a/api/requirements_dev.txt b/api/requirements_dev.txt index d59e62c36..d946c3943 100644 --- a/api/requirements_dev.txt +++ b/api/requirements_dev.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --frozen --group dev --format requirements.txt --no-emit-project --output-file requirements_dev.txt +# uv export --frozen --format requirements-txt --no-emit-project --group dev --output-file requirements_dev.txt alembic==1.18.4 \ --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \ --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc @@ -24,55 +24,35 @@ argon2-cffi-bindings==25.1.0 \ --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ --hash=sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44 \ + --hash=sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a \ + --hash=sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f \ --hash=sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2 \ --hash=sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0 \ + --hash=sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f \ + --hash=sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623 \ + --hash=sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b \ + --hash=sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44 \ --hash=sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98 \ --hash=sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500 \ --hash=sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94 \ + --hash=sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6 \ --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ + --hash=sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85 \ + --hash=sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92 \ --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ - --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a + --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a \ + --hash=sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb # via argon2-cffi astroid==4.0.4 \ --hash=sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753 \ --hash=sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0 # via pylint -async-timeout==5.0.1 ; python_full_version < '3.11.3' \ - --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ - --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 - # via redis attrs==26.1.0 \ --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # jsonschema # referencing -backports-zstd==1.3.0 \ - --hash=sha256:08dfdfb85da5915383bfae680b6ac10ab5769ab22e690f9a854320720011ae8e \ - --hash=sha256:142178fe981061f1d2a57c5348f2cd31a3b6397a35593e7a17dbda817b793a7f \ - --hash=sha256:199eb9bd8aca6a9d489c41a682fad22c587dffe57b613d0fe6d492d0d38ce7c5 \ - --hash=sha256:1df583adc0ae84a8d13d7139f42eade6d90182b1dd3e0d28f7df3c564b9fd55d \ - --hash=sha256:249f90b39d3741c48620021a968b35f268ca70e35f555abeea9ff95a451f35f9 \ - --hash=sha256:2524bd6777a828d5e7ccd7bd1a57f9e7007ae654fc2bd1bc1a207f6428674e4a \ - --hash=sha256:440ef1be06e82dc0d69dbb57177f2ce98bbd2151013ee7e551e2f2b54caa6120 \ - --hash=sha256:5e137657c830a5ce99be40a1d713eb1d246bae488ada28ff0666ac4387aebdd5 \ - --hash=sha256:5eed0a09a163f3a8125a857cb031be87ed052e4a47bc75085ed7fca786e9bb5b \ - --hash=sha256:60aa483fef5843749e993dde01229e5eedebca8c283023d27d6bf6800d1d4ce3 \ - --hash=sha256:676eb5e177d4ef528cf3baaeea4fffe05f664e4dd985d3ac06960ef4619c81a9 \ - --hash=sha256:8aeee9210c54cf8bf83f4d263a6d0d6e7a0298aeb5a14a0a95e90487c5c3157c \ - --hash=sha256:94048c8089755e482e4b34608029cf1142523a625873c272be2b1c9253871a72 \ - --hash=sha256:968167d29f012cee7b112ad031a8925e484e97e99288e55e4d62962c3a1013e3 \ - --hash=sha256:b0e71e83e46154a9d3ced6d4de9a2fea8207ee1e4832aeecf364dc125eda305c \ - --hash=sha256:ba7114a3099e5ea05cbb46568bd0e08bca2ca11e12c6a7b563a24b86b2b4a67f \ - --hash=sha256:cbc6193acd21f96760c94dd71bf32b161223e8503f5277acb0a5ab54e5598957 \ - --hash=sha256:d339c1ec40485e97e600eb9a285fb13169dbf44c5094b945788a62f38b96e533 \ - --hash=sha256:d833fc23aa3cc2e05aeffc7cfadd87b796654ad3a7fb214555cda3f1db2d4dc2 \ - --hash=sha256:d8aac2e7cdcc8f310c16f98a0062b48d0a081dbb82862794f4f4f5bdafde30a4 \ - --hash=sha256:d8f6fc7d62b71083b574193dd8fb3a60e6bb34880cc0132aad242943af301f7a \ - --hash=sha256:e0f2eca6aac280fdb77991ad3362487ee91a7fb064ad40043fb5a0bf5a376943 \ - --hash=sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2 \ - --hash=sha256:ea0886c1b619773544546e243ed73f6d6c2b1ae3c00c904ccc9903a352d731e1 - # via flask-compress bidict==0.23.1 \ --hash=sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71 \ --hash=sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5 @@ -84,29 +64,30 @@ blinker==1.9.0 \ # flask # flask-socketio brotli==1.2.0 ; platform_python_implementation != 'PyPy' \ - --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ - --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ - --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ - --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ - --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ - --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ - --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ - --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ - --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ - --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ - --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a + --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \ + --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \ + --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \ + --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \ + --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \ + --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \ + --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \ + --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \ + --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \ + --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 # via flask-compress brotlicffi==1.2.0.1 ; platform_python_implementation == 'PyPy' \ - --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \ + --hash=sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd \ + --hash=sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec \ + --hash=sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5 \ + --hash=sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000 \ --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \ --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \ --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \ - --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \ --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \ --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \ - --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \ - --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \ - --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1 + --hash=sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac \ + --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede # via flask-compress cachelib==0.13.0 \ --hash=sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48 \ @@ -121,44 +102,69 @@ certifi==2026.2.25 \ # minio # requests cffi==2.0.0 \ - --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ - --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ - --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ - --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ - --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ - --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ - --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ - --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ - --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ - --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ - --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ - --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ - --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 # via # argon2-cffi-bindings # brotlicffi # cryptography # gevent charset-normalizer==3.4.6 \ - --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ + --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ + --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ + --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ + --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ - --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ - --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ - --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ - --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ - --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ + --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ + --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ + --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ + --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ + --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ + --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ + --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ + --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ + --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ + --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ + --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ + --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ + --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ + --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ + --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ - --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ - --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ - --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ - --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ - --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ - --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ - --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ - --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ - --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ - --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed + --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ + --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ + --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ + --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ + --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ + --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ + --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ + --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ + --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ + --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ + --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ + --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ + --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 # via requests click==8.3.1 \ --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ @@ -175,60 +181,83 @@ colorama==0.4.6 ; sys_platform == 'win32' \ # pylint # pytest coverage==7.13.5 \ - --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \ - --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \ - --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \ - --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \ - --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \ + --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \ + --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \ + --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \ + --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \ + --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \ + --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \ + --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \ --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ - --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \ - --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \ - --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \ - --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \ - --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \ - --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \ - --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \ - --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \ + --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \ + --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \ + --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \ + --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \ + --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \ + --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \ + --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \ + --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \ + --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \ + --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \ + --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \ + --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \ + --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \ + --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \ + --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \ --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \ - --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ - --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b + --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \ + --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \ + --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \ + --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \ + --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \ + --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \ + --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \ + --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f # via pytest-cov cryptography==46.0.6 \ --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ + --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ + --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ + --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ + --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ + --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ + --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ + --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ + --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ + --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ + --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ + --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ + --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ + --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 + --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ + --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 # via flask-jwt-oidc dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ @@ -305,26 +334,35 @@ flask-sqlalchemy==3.1.1 \ # flask-migrate # queue-api gevent==25.9.1 \ - --hash=sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7 \ - --hash=sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457 \ - --hash=sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff \ - --hash=sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586 \ - --hash=sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235 \ + --hash=sha256:0adb937f13e5fb90cca2edf66d8d7e99d62a299687400ce2edee3f3504009356 \ + --hash=sha256:1a3fe4ea1c312dbf6b375b416925036fe79a40054e6bf6248ee46526ea628be1 \ + --hash=sha256:1d0f5d8d73f97e24ea8d24d8be0f51e0cf7c54b8021c1fddb580bf239474690f \ + --hash=sha256:427f869a2050a4202d93cf7fd6ab5cffb06d3e9113c10c967b6e2a0d45237cb8 \ --hash=sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd \ - --hash=sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a \ - --hash=sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56 + --hash=sha256:b5a67a0974ad9f24721034d1e008856111e0535f1541499f72a733a73d658d1c \ + --hash=sha256:bb63c0d6cb9950cc94036a4995b9cc4667b8915366613449236970f4394f94d7 \ + --hash=sha256:c049880175e8c93124188f9d926af0a62826a3b81aa6d3074928345f8238279e \ + --hash=sha256:ddd3ff26e5c4240d3fbf5516c2d9d5f2a998ef87cfb73e1429cfaeaaec860fa6 # via queue-api greenlet==3.3.2 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or platform_python_implementation == 'CPython' \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ + --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ + --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ + --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 + --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ + --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ + --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ + --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ + --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ + --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ + --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ + --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ + --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ + --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ + --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ + --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ + --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ + --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 # via # gevent # sqlalchemy @@ -381,18 +419,29 @@ mako==1.3.10 \ --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 # via alembic markupsafe==3.0.3 \ - --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ - --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ - --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ - --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ - --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ - --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ - --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ - --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ - --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ - --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ - --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via # flask # flask-admin @@ -438,18 +487,18 @@ pluggy==1.6.0 \ # pytest # pytest-cov psycopg2-binary==2.9.11 \ - --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \ - --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \ - --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \ - --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \ - --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \ - --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \ - --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \ + --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \ + --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \ + --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \ + --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \ + --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \ + --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \ --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \ - --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \ - --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \ - --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \ - --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 + --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \ + --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \ + --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \ + --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \ + --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747 # via queue-api pyasn1==0.6.3 \ --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ @@ -531,34 +580,36 @@ requests==2.33.0 \ # queue-api # snowplow-tracker rpds-py==0.30.0 \ - --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \ - --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \ - --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \ - --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \ - --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \ - --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \ - --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \ - --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \ - --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \ - --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \ - --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \ - --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \ - --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \ - --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \ - --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \ - --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \ - --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \ - --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \ - --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \ - --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \ - --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \ - --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \ - --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \ - --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \ + --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \ + --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \ + --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \ + --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \ + --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \ + --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \ + --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \ + --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \ + --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \ + --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \ + --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \ + --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \ + --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \ + --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \ + --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \ + --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \ + --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \ + --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \ + --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \ + --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \ + --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \ + --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \ + --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \ + --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \ --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \ - --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \ - --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \ - --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a + --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \ + --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \ + --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \ + --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \ + --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5 # via # jsonschema # referencing @@ -566,6 +617,25 @@ rsa==4.9.1 \ --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 # via python-jose +ruff==0.15.8 \ + --hash=sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89 \ + --hash=sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1 \ + --hash=sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3 \ + --hash=sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8 \ + --hash=sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762 \ + --hash=sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3 \ + --hash=sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49 \ + --hash=sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb \ + --hash=sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e \ + --hash=sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec \ + --hash=sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34 \ + --hash=sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8 \ + --hash=sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6 \ + --hash=sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7 \ + --hash=sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2 \ + --hash=sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570 \ + --hash=sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a \ + --hash=sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94 simple-websocket==1.1.0 \ --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 @@ -581,33 +651,26 @@ snowplow-tracker==1.1.0 \ --hash=sha256:95d8fdc8bd542fd12a0b9a076852239cbaf0599eda8721deaf5f93f7138fe755 # via queue-api sqlalchemy==2.0.48 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ + --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ + --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ + --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ + --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ + --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ + --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ + --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ + --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ + --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c + --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ + --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ + --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ + --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # queue-api -tomli==2.4.1 ; python_full_version <= '3.11' \ - --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ - --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ - --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ - --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ - --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ - --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ - --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ - --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ - --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ - --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ - --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 - # via coverage tomlkit==0.14.0 \ --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 @@ -618,7 +681,6 @@ typing-extensions==4.15.0 \ # via # alembic # minio - # referencing # snowplow-tracker # sqlalchemy tzdata==2025.3 \ @@ -662,11 +724,11 @@ zope-event==6.1 \ --hash=sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0 # via gevent zope-interface==8.2 \ - --hash=sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489 \ - --hash=sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c \ - --hash=sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b \ + --hash=sha256:05a0e42d6d830f547e114de2e7cd15750dc6c0c78f8138e6c5035e51ddfff37c \ + --hash=sha256:46c7e4e8cbc698398a67e56ca985d19cb92365b4aafbeb6a712e8c101090f4cb \ + --hash=sha256:561ce42390bee90bae51cf1c012902a8033b2aaefbd0deed81e877562a116d48 \ + --hash=sha256:a87fc7517f825a97ff4a4ca4c8a950593c59e0f8e7bfe1b6f898a38d5ba9f9cf \ + --hash=sha256:aae807efc7bd26302eb2fea05cd6de7d59269ed6ae23a6de1ee47add6de99b8c \ --hash=sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224 \ - --hash=sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322 \ - --hash=sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce \ - --hash=sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466 + --hash=sha256:ccf52f7d44d669203c2096c1a0c2c15d52e36b2e7a9413df50f48392c7d4d080 # via gevent From a1dc3038c6c1f6da3cf1d61c806a6b39fb53f662 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 31 Mar 2026 12:20:53 -0700 Subject: [PATCH 28/81] Add Dockerfile --- .gitignore | 3 +-- README.md | 14 +++++++++++--- api/Dockerfile | 25 +++++++++++++++++++++++++ compose.yaml | 24 ++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 api/Dockerfile diff --git a/.gitignore b/.gitignore index a5b6d5e95..7403da480 100644 --- a/.gitignore +++ b/.gitignore @@ -149,7 +149,6 @@ version.GENERATED.ts # Docker -api/Dockerfile frontend/Dockerfile api/init.sh api/wait-for-it.sh @@ -179,4 +178,4 @@ keys-generated # new config stuff /frontend/public/config/configuration.json -/appointment-frontend/public/config/configuration.json \ No newline at end of file +/appointment-frontend/public/config/configuration.json diff --git a/README.md b/README.md index 89bda694b..5009b3a99 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ The devcontainer installs dependencies automatically, applies database migration cp ./.devcontainer/config/appointment-frontend/public/config/configuration.json ./appointment-frontend/public/config/configuration.json ``` -4. Start the local auth server and PostgreSQL: +4. Start the local auth server: ```bash docker compose up -d keycloak @@ -171,7 +171,7 @@ The devcontainer installs dependencies automatically, applies database migration - Confidential client id: `theq-queue-management-api` - Confidential client secret: `theq-local-dev-secret` -5. Make sure the database settings in `api/.env` point to your local database. +5. Make sure the database settings in `api/.env` point to your local PostgreSQL instance. 6. Run database migrations: @@ -184,13 +184,21 @@ The devcontainer installs dependencies automatically, applies database migration Start the services in separate terminals. -Queue management API: +Queue management API using the local Python environment: ```bash cd ./api uv run gunicorn wsgi --bind=0.0.0.0:5000 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 ``` +Queue management API using the production Dockerfile through Compose: + +```bash +docker compose --profile api up --build api +``` + +The optional `api` Compose service still serves the application on `http://localhost:5000`. It reads `api/.env`, then overrides container-only settings so it can reach the host PostgreSQL and host-run notifications API while continuing to use the local Keycloak on `http://localhost:8085/auth`. + Notifications API: ```bash diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 000000000..516c2c7ce --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,25 @@ +FROM python:3.14-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 + +WORKDIR /opt/app-root/src + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + gcc \ + libpq-dev \ + libmagic1 \ + libmagic-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --upgrade pip setuptools wheel && \ + pip install -r requirements.txt + +COPY . . + +EXPOSE 8080 + +CMD ["gunicorn", "wsgi", "--bind", "0.0.0.0:8080", "--access-logfile=-", "--config=gunicorn_config.py"] diff --git a/compose.yaml b/compose.yaml index 0dfab38ee..e9c407c65 100644 --- a/compose.yaml +++ b/compose.yaml @@ -14,3 +14,27 @@ services: - "8085:8080" volumes: - ./keycloak-local:/opt/keycloak/data/import:ro + + api: + profiles: + - api + build: + context: ./api + dockerfile: Dockerfile + depends_on: + - keycloak + env_file: + - ./api/.env + environment: + FLASK_CONFIGURATION: localhost + DATABASE_HOST: host.docker.internal + DATABASE_PORT: "5432" + NOTIFICATIONS_ENDPOINT: http://host.docker.internal:5002/api/v1/notifications/sms + NOTIFICATIONS_EMAIL_ENDPOINT: http://host.docker.internal:5002/api/v1/notifications/email + JWT_OIDC_WELL_KNOWN_CONFIG: "" + JWT_OIDC_JWKS_URI: http://keycloak:8080/auth/realms/servicebc-local/protocol/openid-connect/certs + JWT_OIDC_ISSUER: http://localhost:8085/auth/realms/servicebc-local + extra_hosts: + - host.docker.internal:host-gateway + ports: + - "5000:8080" From 5289de1414c5739c674dfbc88cb4d31ce49a5972 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 31 Mar 2026 12:30:22 -0700 Subject: [PATCH 29/81] Fix README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5009b3a99..e2c98f296 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ The devcontainer installs dependencies automatically, applies database migration - Base URL: `http://localhost:8085/auth` - Admin console: `http://localhost:8085/auth/admin/` - Admin credentials: `admin` / `password` - - Demo users: `csr@idir`, `ga@idir`, `support@idir`, `citizen@bceidboth` + - Demo users: `democsr@idir`, `demoga@idir`, `admin@idir`, `citizen@bceidboth` - Demo user password: `password` - Confidential client id: `theq-queue-management-api` - Confidential client secret: `theq-local-dev-secret` From 6f941ac77f526aa1f366c7d382874c86fdb66c46 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 31 Mar 2026 13:12:36 -0700 Subject: [PATCH 30/81] Add notification API client to local realm --- keycloak-local/servicebc-local-realm.json | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/keycloak-local/servicebc-local-realm.json b/keycloak-local/servicebc-local-realm.json index 39094e375..c0d2eac7a 100644 --- a/keycloak-local/servicebc-local-realm.json +++ b/keycloak-local/servicebc-local-realm.json @@ -53,6 +53,17 @@ "access.token.claim": "true" } }, + { + "name": "audience-theq-notifications-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-notifications-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, { "name": "username", "protocol": "openid-connect", @@ -186,6 +197,17 @@ "access.token.claim": "true" } }, + { + "name": "audience-theq-notifications-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-notifications-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, { "name": "username", "protocol": "openid-connect", @@ -408,6 +430,14 @@ } } ] + }, + { + "clientId": "theq-notifications-api", + "name": "TheQ Notifications API", + "description": "Notifications API Client for The Q (SBC)", + "enabled": true, + "protocol": "openid-connect", + "bearerOnly": true } ], "users": [ From 13f933ce0ec270f860d0d01bbccf91c9b32094dc Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 31 Mar 2026 16:13:21 -0700 Subject: [PATCH 31/81] Refactor and improve test coverage --- .../bookings/booking/booking_post.py | 2 +- .../resources/bookings/booking/booking_put.py | 2 +- api/app/resources/bookings/exam/exam_bcmp.py | 8 +- .../bookings/exam/exam_bulk_status.py | 7 +- .../bookings/exam/exam_email_invigilator.py | 4 +- .../bookings/exam/exam_export_list.py | 7 +- api/app/resources/bookings/exam/exam_post.py | 31 +- api/app/resources/theq/services.py | 5 +- api/app/services/availability_service.py | 9 +- api/app/tests/README.md | 159 ++++ api/app/tests/api_test_support.py | 343 ++++++- api/app/tests/auth/__init__.py | 1 + api/app/tests/auth/auth_support.py | 271 ++++++ .../auth/test_authenticated_only_routes.py | 212 +++++ api/app/tests/auth/test_cookie_routes.py | 19 + .../tests/auth/test_internal_only_routes.py | 878 ++++++++++++++++++ api/app/tests/auth/test_mixed_role_routes.py | 152 +++ api/app/tests/auth/test_open_routes.py | 171 ++++ api/app/tests/auth/test_public_user_routes.py | 120 +++ .../tests/auth/test_reminder_job_routes.py | 38 + api/app/tests/conftest.py | 438 +-------- api/app/tests/contracts/__init__.py | 1 + api/app/tests/contracts/conftest.py | 17 + api/app/tests/contracts/schemas.py | 817 ++++++++++++++++ .../contracts/test_appointment_contracts.py | 149 +++ .../tests/contracts/test_booking_contracts.py | 77 ++ .../tests/contracts/test_citizen_contracts.py | 79 ++ .../tests/contracts/test_contract_helpers.py | 46 + .../contracts/test_contract_strictness.py | 138 +++ .../tests/contracts/test_exam_contracts.py | 118 +++ .../contracts/test_public_api_contracts.py | 28 + .../test_reference_data_contracts.py | 346 +++++++ .../contracts/test_reminder_contracts.py | 64 ++ api/app/tests/fixtures/__init__.py | 1 + api/app/tests/fixtures/auth.py | 41 + api/app/tests/fixtures/db.py | 474 ++++++++++ api/app/tests/fixtures/smoke.py | 10 + api/app/tests/flows/__init__.py | 1 + api/app/tests/flows/test_appointment_flows.py | 252 +++++ .../tests/flows/test_booking_exam_flows.py | 272 ++++++ ..._booking_recurring_current_office_flows.py | 96 ++ .../tests/flows/test_csr_dashboard_flows.py | 160 ++++ api/app/tests/flows/test_csr_flows.py | 98 ++ .../flows/test_draft_appointment_flows.py | 132 +++ api/app/tests/flows/test_exam_export_flows.py | 180 ++++ .../flows/test_exam_integration_flows.py | 111 +++ .../tests/flows/test_exam_variant_flows.py | 314 +++++++ api/app/tests/flows/test_queue_flows.py | 681 ++++++++++++++ api/app/tests/flows/test_reminder_flows.py | 83 ++ .../tests/flows/test_service_refresh_flows.py | 47 + .../tests/flows/test_service_request_flows.py | 184 ++++ .../flows/test_walkin_smartboard_flows.py | 377 ++++++++ api/app/tests/helpers/__init__.py | 1 + api/app/tests/helpers/appointments.py | 71 ++ api/app/tests/helpers/exams.py | 30 + api/app/tests/test_api_contracts.py | 74 -- api/app/tests/test_appointment_flows.py | 168 ---- api/app/tests/test_availability_service.py | 248 +++++ api/app/tests/test_booking_exam_flows.py | 189 ---- api/app/tests/test_citizen_finish_service.py | 9 - api/app/tests/test_flask3_admin.py | 121 +++ api/app/tests/test_flask3_smoke.py | 77 -- .../tests/test_flask_admin_wtforms_compat.py | 66 -- api/app/tests/test_notification.py | 40 + api/app/tests/test_queue_flows.py | 301 ------ api/app/tests/test_schema_compatibility.py | 17 +- ...sqlalchemy_smoke.py => test_sqlalchemy.py} | 12 +- ...ernization.py => test_timezone_helpers.py} | 43 +- api/app/tests/test_websocket_smoke.py | 194 ++++ api/app/tests/validation/__init__.py | 1 + .../validation/test_appointment_validation.py | 150 +++ .../validation/test_booking_validation.py | 37 + .../tests/validation/test_csr_validation.py | 59 ++ .../tests/validation/test_exam_validation.py | 47 + .../test_service_refresh_validation.py | 20 + ...y_smoke_tests.sh => run_api_full_tests.sh} | 2 +- ..._tests.sh => run_api_integration_tests.sh} | 7 +- api/scripts/run_api_smoke_tests.sh | 8 + api/setup.cfg | 6 +- 79 files changed, 8909 insertions(+), 1360 deletions(-) create mode 100644 api/app/tests/README.md create mode 100644 api/app/tests/auth/__init__.py create mode 100644 api/app/tests/auth/auth_support.py create mode 100644 api/app/tests/auth/test_authenticated_only_routes.py create mode 100644 api/app/tests/auth/test_cookie_routes.py create mode 100644 api/app/tests/auth/test_internal_only_routes.py create mode 100644 api/app/tests/auth/test_mixed_role_routes.py create mode 100644 api/app/tests/auth/test_open_routes.py create mode 100644 api/app/tests/auth/test_public_user_routes.py create mode 100644 api/app/tests/auth/test_reminder_job_routes.py create mode 100644 api/app/tests/contracts/__init__.py create mode 100644 api/app/tests/contracts/conftest.py create mode 100644 api/app/tests/contracts/schemas.py create mode 100644 api/app/tests/contracts/test_appointment_contracts.py create mode 100644 api/app/tests/contracts/test_booking_contracts.py create mode 100644 api/app/tests/contracts/test_citizen_contracts.py create mode 100644 api/app/tests/contracts/test_contract_helpers.py create mode 100644 api/app/tests/contracts/test_contract_strictness.py create mode 100644 api/app/tests/contracts/test_exam_contracts.py create mode 100644 api/app/tests/contracts/test_public_api_contracts.py create mode 100644 api/app/tests/contracts/test_reference_data_contracts.py create mode 100644 api/app/tests/contracts/test_reminder_contracts.py create mode 100644 api/app/tests/fixtures/__init__.py create mode 100644 api/app/tests/fixtures/auth.py create mode 100644 api/app/tests/fixtures/db.py create mode 100644 api/app/tests/fixtures/smoke.py create mode 100644 api/app/tests/flows/__init__.py create mode 100644 api/app/tests/flows/test_appointment_flows.py create mode 100644 api/app/tests/flows/test_booking_exam_flows.py create mode 100644 api/app/tests/flows/test_booking_recurring_current_office_flows.py create mode 100644 api/app/tests/flows/test_csr_dashboard_flows.py create mode 100644 api/app/tests/flows/test_csr_flows.py create mode 100644 api/app/tests/flows/test_draft_appointment_flows.py create mode 100644 api/app/tests/flows/test_exam_export_flows.py create mode 100644 api/app/tests/flows/test_exam_integration_flows.py create mode 100644 api/app/tests/flows/test_exam_variant_flows.py create mode 100644 api/app/tests/flows/test_queue_flows.py create mode 100644 api/app/tests/flows/test_reminder_flows.py create mode 100644 api/app/tests/flows/test_service_refresh_flows.py create mode 100644 api/app/tests/flows/test_service_request_flows.py create mode 100644 api/app/tests/flows/test_walkin_smartboard_flows.py create mode 100644 api/app/tests/helpers/__init__.py create mode 100644 api/app/tests/helpers/appointments.py create mode 100644 api/app/tests/helpers/exams.py delete mode 100644 api/app/tests/test_api_contracts.py delete mode 100644 api/app/tests/test_appointment_flows.py create mode 100644 api/app/tests/test_availability_service.py delete mode 100644 api/app/tests/test_booking_exam_flows.py delete mode 100644 api/app/tests/test_citizen_finish_service.py create mode 100644 api/app/tests/test_flask3_admin.py delete mode 100644 api/app/tests/test_flask3_smoke.py delete mode 100644 api/app/tests/test_flask_admin_wtforms_compat.py create mode 100644 api/app/tests/test_notification.py delete mode 100644 api/app/tests/test_queue_flows.py rename api/app/tests/{test_sqlalchemy_smoke.py => test_sqlalchemy.py} (91%) rename api/app/tests/{test_dependency_modernization.py => test_timezone_helpers.py} (51%) create mode 100644 api/app/tests/test_websocket_smoke.py create mode 100644 api/app/tests/validation/__init__.py create mode 100644 api/app/tests/validation/test_appointment_validation.py create mode 100644 api/app/tests/validation/test_booking_validation.py create mode 100644 api/app/tests/validation/test_csr_validation.py create mode 100644 api/app/tests/validation/test_exam_validation.py create mode 100644 api/app/tests/validation/test_service_refresh_validation.py rename api/scripts/{run_sqlalchemy_smoke_tests.sh => run_api_full_tests.sh} (75%) rename api/scripts/{run_sqlalchemy_warn20_tests.sh => run_api_integration_tests.sh} (50%) create mode 100755 api/scripts/run_api_smoke_tests.sh diff --git a/api/app/resources/bookings/booking/booking_post.py b/api/app/resources/bookings/booking/booking_post.py index 64325b6a5..faa135874 100644 --- a/api/app/resources/bookings/booking/booking_post.py +++ b/api/app/resources/bookings/booking/booking_post.py @@ -82,4 +82,4 @@ def post(self): return {"booking": result, "errors": self.booking_schema.validate(booking)}, 201 else: - return {"The Booking Office ID and CSR Office ID do not match!"}, 403 + return {"message": "The Booking Office ID and CSR Office ID do not match!"}, 403 diff --git a/api/app/resources/bookings/booking/booking_put.py b/api/app/resources/bookings/booking/booking_put.py index 39b21106b..d61f04862 100644 --- a/api/app/resources/bookings/booking/booking_put.py +++ b/api/app/resources/bookings/booking/booking_put.py @@ -83,4 +83,4 @@ def put(self, id): "errors": self.booking_schema.validate(booking)}, 200 else: - return {"The Booking Office ID and the CSR Office ID do not match!"}, 403 + return {"message": "The Booking Office ID and the CSR Office ID do not match!"}, 403 diff --git a/api/app/resources/bookings/exam/exam_bcmp.py b/api/app/resources/bookings/exam/exam_bcmp.py index c07c3411d..a56c1c39f 100644 --- a/api/app/resources/bookings/exam/exam_bcmp.py +++ b/api/app/resources/bookings/exam/exam_bcmp.py @@ -39,21 +39,22 @@ def post(self): csr = CSR.find_by_username(get_username()) json_data = request.get_json() + exam_payload = ExamPost.model_payload(json_data) if 'bookdata' in json_data.keys(): booking = json_data["bookdata"] else: booking = None - exam = self.exam_schema.load(json_data) - warning = self.exam_schema.validate(json_data) + exam = self.exam_schema.load(exam_payload) + warning = self.exam_schema.validate(exam_payload) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 if not (exam.office_id == csr.office_id or csr.ita2_designate == 1): - return {"The Exam Office ID and CSR Office ID do not match!"}, 403 + return {"message": "The Exam Office ID and CSR Office ID do not match!"}, 403 formatted_data = ExamPost.format_data(self, json_data, exam) exam = formatted_data["exam"] @@ -82,4 +83,3 @@ def post(self): else: return {"message": "create_group_exam_bcmp failed", "error": bcmp_response}, 403 - diff --git a/api/app/resources/bookings/exam/exam_bulk_status.py b/api/app/resources/bookings/exam/exam_bulk_status.py index a101fbdb9..9924a6a2c 100644 --- a/api/app/resources/bookings/exam/exam_bulk_status.py +++ b/api/app/resources/bookings/exam/exam_bulk_status.py @@ -45,14 +45,15 @@ def post(self): my_print("job_ids to update: ") my_print(job_ids) - exams_tobe_updated = None + updated_exam_ids = [] if len(job_ids) != 0: - exams_tobe_updated = Exam.query.filter(Exam.bcmp_job_id.in_(job_ids)) + exams_tobe_updated = Exam.query.filter(Exam.bcmp_job_id.in_(job_ids)).all() for exam in exams_tobe_updated: exam_upd = self.exam_schema.load({'upload_received_ind': 1}, instance=exam, partial=True) db.session.add(exam_upd) + updated_exam_ids.append(exam.exam_id) try: db.session.commit() @@ -60,7 +61,7 @@ def post(self): db.session.rollback() raise - return {"exams_updated": exams_tobe_updated}, 200 + return {"exams_updated": updated_exam_ids}, 200 except exc.SQLAlchemyError as error: logging.error(error, exc_info=True) diff --git a/api/app/resources/bookings/exam/exam_email_invigilator.py b/api/app/resources/bookings/exam/exam_email_invigilator.py index c75ba69ba..1ac798254 100644 --- a/api/app/resources/bookings/exam/exam_email_invigilator.py +++ b/api/app/resources/bookings/exam/exam_email_invigilator.py @@ -37,7 +37,7 @@ def post(self, exam_id): exam = Exam.query.filter_by(exam_id=exam_id).first() if not (exam.office_id == csr.office_id or csr.ita2_designate == 1): - return {"The Exam Office ID and CSR Office ID do not match!"}, 403 + return {"message": "The Exam Office ID and CSR Office ID do not match!"}, 403 json_data = request.get_json() invigilator_id = json_data["invigilator_id"] @@ -46,7 +46,7 @@ def post(self, exam_id): invigilator_phone = json_data["invigilator_phone"] if not invigilator_email or not invigilator_phone or not invigilator_name: - return {"Invigilator name, email, and phone number are required"}, 422 + return {"message": "Invigilator name, email, and phone number are required"}, 422 response = self.bcmp_service.email_exam_invigilator( exam, diff --git a/api/app/resources/bookings/exam/exam_export_list.py b/api/app/resources/bookings/exam/exam_export_list.py index 169e1ce1e..eb2faa83d 100644 --- a/api/app/resources/bookings/exam/exam_export_list.py +++ b/api/app/resources/bookings/exam/exam_export_list.py @@ -48,7 +48,9 @@ def get(self): end_param = request.args.get("end_date") exam_type = request.args.get("exam_type") - validate_params(start_param, end_param) + validation_error = validate_params(start_param, end_param) + if validation_error: + return validation_error try: start_date = datetime.strptime(request.args['start_date'], "%Y-%m-%d") @@ -56,7 +58,7 @@ def get(self): except ValueError as exception: logging.exception(exception) - return {"message", "Unable to return date time string"}, 422 + return {"message": "Unable to return date time string"}, 422 # Code for UTC time. csr_office = Office.query.filter(Office.office_id == csr.office_id).first() @@ -339,6 +341,7 @@ def write_exam_returned(row, exam): def validate_params(start_param, end_param): if not (start_param and end_param): return {"message": "Must provide both start and end time"}, 422 + return None def write_non_exam_name(booking, row): diff --git a/api/app/resources/bookings/exam/exam_post.py b/api/app/resources/bookings/exam/exam_post.py index bf3acba9f..c9e279679 100644 --- a/api/app/resources/bookings/exam/exam_post.py +++ b/api/app/resources/bookings/exam/exam_post.py @@ -33,6 +33,29 @@ class ExamPost(Resource): exam_schema = ExamSchema() bcmp_service = BCMPService() + @staticmethod + def model_payload(json_data): + """Strip request-only BCMP fields before loading a SQLAlchemy Exam model.""" + payload = dict(json_data) + + for field_name in ( + "bookdata", + "candidates", + "fees", + "ind_or_group", + "receipt_number", + "sbc_managed", + ): + payload.pop(field_name, None) + + if "receipt" not in payload and json_data.get("receipt_number") is not None: + payload["receipt"] = json_data["receipt_number"] + + if "sbc_managed_ind" not in payload and "sbc_managed" in json_data: + payload["sbc_managed_ind"] = 1 if json_data["sbc_managed"] == "sbc" else 0 + + return payload + @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self): @@ -45,9 +68,10 @@ def post(self): csr = CSR.find_by_username(get_username()) json_data = request.get_json() + exam_payload = self.model_payload(json_data) - exam = self.exam_schema.load(json_data) - warning = self.exam_schema.validate(json_data) + exam = self.exam_schema.load(exam_payload) + warning = self.exam_schema.validate(exam_payload) my_print("json_data: ") my_print(json_data) @@ -57,7 +81,7 @@ def post(self): return {"message": warning}, 422 if not (exam.office_id == csr.office_id or csr.ita2_designate == 1): - return {"The Exam Office ID and CSR Office ID do not match!"}, 403 + return {"message": "The Exam Office ID and CSR Office ID do not match!"}, 403 if exam.is_pesticide: formatted_data = self.format_data(json_data, exam) @@ -134,4 +158,3 @@ def format_data(self, json_data, exam): 'candidates_list_bcmp': candidates_list_bcmp, 'pesticide_office': pesticide_office, } - diff --git a/api/app/resources/theq/services.py b/api/app/resources/theq/services.py index 6978598a3..0d10a1ea8 100644 --- a/api/app/resources/theq/services.py +++ b/api/app/resources/theq/services.py @@ -37,7 +37,10 @@ class Refresh(Resource): @jwt.has_one_of_roles([Role.internal_user.value]) def get(self): if request.args.get('office_id'): - office_id = int(request.args.get('office_id')) + try: + office_id = int(request.args.get('office_id')) + except ValueError: + return {'message': 'office_id must be an integer.'}, 400 csr = CSR.find_by_username(get_username()) if csr.role.role_code == "GA": diff --git a/api/app/services/availability_service.py b/api/app/services/availability_service.py index c2987df38..2eebfc91f 100644 --- a/api/app/services/availability_service.py +++ b/api/app/services/availability_service.py @@ -52,6 +52,7 @@ def get_available_slots(office: Office, days: [datetime], format_time: bool = Tr # soonest a citizen can book an appointment soonest_appointment_date = today + datetime.timedelta(minutes = office.soonest_appointment or 0) + soonest_appointment_time = soonest_appointment_date.timetz() # Find all appointments between the dates appointments = Appointment.find_appointment_availability(office_id=office.office_id, first_date=today, @@ -91,7 +92,7 @@ def get_available_slots(office: Office, days: [datetime], format_time: bool = Tr } # Check if today's time is past appointment slot # Arc - also check if in office.soonest_appointment - if ((day_in_month.date() == soonest_appointment_date.date() and start_time >= soonest_appointment_date.time()) or day_in_month.date() > soonest_appointment_date.date()) and slot not in available_slots_per_day[formatted_date]: + if ((day_in_month.date() == soonest_appointment_date.date() and start_time >= soonest_appointment_time) or day_in_month.date() > soonest_appointment_date.date()) and slot not in available_slots_per_day[formatted_date]: available_slots_per_day[formatted_date].append(slot) start_time = end_time.replace(tzinfo=tz) @@ -159,7 +160,7 @@ def has_available_slots(office: Office, start_time:datetime, end_time: datetime, # Because services can be artbitary duration, we just check if times fall within duration # e.g slot is 8-9, but start_time/end_time are 8:30-8:45. # We do NOT check across slots, only within an individual slot - if slot['start_time'] <= start_time.time() and slot['end_time'] >= end_time.time(): + if slot['start_time'] <= start_time.timetz() and slot['end_time'] >= end_time.timetz(): has_available_slot = True return has_available_slot @@ -173,8 +174,8 @@ def group_appointments(appointments, timezone: str): if not filtered_appointments.get(formatted_date, None): filtered_appointments[formatted_date] = [] filtered_appointments[formatted_date].append({ - 'start_time': app.start_time.astimezone(app_timezone).time(), - 'end_time': app.end_time.astimezone(app_timezone).time(), + 'start_time': app.start_time.astimezone(app_timezone).timetz(), + 'end_time': app.end_time.astimezone(app_timezone).timetz(), 'blackout_flag': app.blackout_flag == 'Y' or app.stat_flag, 'is_dlkt': (app.service.is_dlkt == YesNo.YES) if app.service else False }) diff --git a/api/app/tests/README.md b/api/app/tests/README.md new file mode 100644 index 000000000..222d50414 --- /dev/null +++ b/api/app/tests/README.md @@ -0,0 +1,159 @@ +# API Test Suite Guide + +This suite covers the active API surface used by `/frontend` and +`/appointment-frontend`. The suite is split into a DB-free `smoke` +slice and a Postgres-backed `integration` slice. + +## Supported Run Modes + +From the API root: + +```sh +cd /Users/csampson/Developer/Repositories/queue-management/api +``` + +Smoke-only, DB-free: + +```sh +./scripts/run_api_smoke_tests.sh +``` + +Integration-only, fail fast if disposable Postgres is unavailable: + +```sh +./scripts/run_api_integration_tests.sh +``` + +Full suite, including the default line and branch coverage reports: + +```sh +./scripts/run_api_full_tests.sh +``` + +Equivalent direct pytest commands: + +```sh +uv run pytest app/tests -m smoke -q --override-ini "addopts=--strict-markers" +uv run pytest app/tests -m integration -q --override-ini "addopts=--strict-markers" --require-integration-db +uv run pytest app/tests -q --require-integration-db +``` + +Marker-specific runs still work during local debugging: + +```sh +uv run pytest app/tests -m contracts -q --override-ini "addopts=--strict-markers" +uv run pytest app/tests -m flows -q --override-ini "addopts=--strict-markers" +uv run pytest app/tests -m validation -q --override-ini "addopts=--strict-markers" +uv run pytest app/tests -m "contracts and integration" -q --override-ini "addopts=--strict-markers" --require-integration-db +``` + +## Smoke Vs Integration + +`smoke` is the reliable local gate. It does not require disposable Postgres and +should stay free of DB-dependent skips. + +`integration` is the seeded disposable-Postgres suite. It covers the auth, +contracts, flows, validation, and DB-backed modernization checks that exercise +the live application stack. + +The `--require-integration-db` flag makes Postgres dependency failures loud. If +the DB cannot be created or reached, pytest exits immediately instead of +collapsing the integration slice into a wall of skips. + +If you intentionally want a best-effort local run that allows integration tests +to skip when Postgres is unavailable, `uv run pytest app/tests -q` still works. +That mode is useful for quick local debugging, but it should not be treated as +rewrite-readiness coverage because DB-backed skips can mask missing regression +signal. + +The default pytest configuration now records both line and branch coverage and +writes reports to `htmlcov/` and `coverage.xml`. The explicit marker-focused +commands above override `addopts` so local debugging stays fast and uncluttered. + +## Harness Layout + +| Path | Purpose | +| --- | --- | +| `conftest.py` | Thin pytest plugin loader plus marker/collection policy | +| `fixtures/db.py` | Disposable Postgres, app boot, migrations, and seeded data | +| `fixtures/auth.py` | Authenticated client factories and identity fixtures | +| `fixtures/smoke.py` | Minimal Flask helpers for DB-free smoke tests | +| `contracts/schemas.py` | Shared response schema registry for contract tests | +| `contracts/conftest.py` | Contract validation helper only | +| `helpers/appointments.py` | Reminder and appointment test helpers | +| `helpers/exams.py` | Exam integration test helpers | +| `auth/auth_support.py` | Route builders, seeded auth helpers, integration stubs | + +## Test Coverage + +- Auth boundaries across public, mixed-role, authenticated-only, and + internal-only routes +- Frontend-facing contracts for appointments, users, bookings, exams, + citizens, service requests, and reference data +- Explicit frontend route contracts for `/csrs/`, `/rooms/?office_id=...`, + `/invigilators/offsite/`, and `/smardboard/side-menu/` +- Office-scoped service-list coverage for `/services/?office_id=...`, + including filtering of deleted services and stable frontend ordering +- CSR dashboard coverage for `/csrs/me/`, including `attention_needed`, + `active_citizens`, and rewrite-critical feature flags consumed by + `/frontend` +- Core appointment, draft, booking, exam, queue, and CSR flows +- Current-office recurring booking delete coverage, including same-office + deletion rules and the current-day `<= 5am` preservation exception +- Walk-in and smartboard behavior coverage for queue grouping, payload shaping, + and reminder side effects used by `/appointment-frontend` and smartboard UIs +- Direct availability-service coverage for DST grouping, `soonest_appointment`, + blackout pruning, DLKT slot caps, and overlap checks +- Reminder payload contracts and behavior for email and SMS reminder jobs +- Service-request creation branches for missing payloads, category rejection, + first-service ticket numbering, and additional-service Snowplow transitions +- Service refresh behavior for GA and SUPPORT roles +- Active exam integration flows for BCMP create/status, transfer, download, and + invigilator email +- Pesticide and group-exam creation coverage, including candidate + normalization, non-SBC office reassignment, and exam export filter variants +- Exam export coverage for fail-fast validation, localized CSV timestamps, + blank room/invigilator fields, and designate-vs-office-scoped exports +- DB-free websocket smoke coverage for join-room, smartboard-room, and cache + handlers +- Flask 3, Marshmallow 4, WTForms, timezone, and SQLAlchemy modernization smoke + coverage + +## Contract Strictness Policy + +The schema registry in `contracts/schemas.py` treats frontend-owned +envelopes and stable nested objects as closed contracts by using +`additionalProperties: False`. + +Strict by default for: + +- appointments +- public users +- bookings +- exams +- citizens and service requests +- offices, services, categories, channels, rooms, invigilators, exam types + +Intentionally permissive where the payload shape is genuinely variable: + +- date-keyed slot maps +- office `timeslots` +- BCMP and candidate payloads that the frontend does not treat as fixed + contracts + +`contracts/test_contract_strictness.py` guards against accidental schema +loosening by injecting unexpected fields into valid payloads and asserting that +validation fails. + +## Deprecated And Out-Of-Scope Surface + +The following routes should not affect rewrite-readiness scoring: + +- `/feedback/` +- `/slack/` +- upload routes +- video routes + +Deprecated auth coverage that still exists for upload or video routes should be +treated as cleanup follow-up, not as a blocker for the active rewrite safety +net. diff --git a/api/app/tests/api_test_support.py b/api/app/tests/api_test_support.py index 92b04a768..87188816f 100644 --- a/api/app/tests/api_test_support.py +++ b/api/app/tests/api_test_support.py @@ -10,8 +10,8 @@ @dataclass class ApiClient: client: Any - identity_name: str - token: str = "theq-test-token" + identity_name: Optional[str] + token: Optional[str] = "theq-test-token" def _normalize_path(self, path: str) -> str: if path.startswith("/api/"): @@ -21,10 +21,11 @@ def _normalize_path(self, path: str) -> str: return f"/api/v1/{path.lstrip('/')}" def _headers(self, headers: Optional[dict[str, str]] = None) -> dict[str, str]: - merged = { - "Authorization": f"Bearer {self.token}", - "X-TheQ-Test-Identity": self.identity_name, - } + merged: dict[str, str] = {} + if self.token: + merged["Authorization"] = f"Bearer {self.token}" + if self.identity_name: + merged["X-TheQ-Test-Identity"] = self.identity_name if headers: merged.update(headers) return merged @@ -54,7 +55,27 @@ def assert_status(response, expected_status: int): assert response.status_code == expected_status, response.get_data(as_text=True) -def flatten_slots(slots_by_day: dict[str, list[dict[str, Any]]]) -> list[tuple[str, dict[str, Any]]]: +def assert_json_response(response, expected_status: int): + assert_status(response, expected_status) + content_type = response.headers.get("Content-Type", "") + assert content_type.startswith("application/json"), content_type + + +def assert_unauthorized(response): + assert_status(response, 401) + + +def assert_forbidden(response): + assert_status(response, 403) + + +def assert_not_auth_error(response): + assert response.status_code not in {401, 403}, response.get_data(as_text=True) + + +def flatten_slots( + slots_by_day: dict[str, list[dict[str, Any]]], +) -> list[tuple[str, dict[str, Any]]]: flattened: list[tuple[str, dict[str, Any]]] = [] for day, slots in slots_by_day.items(): for slot in slots: @@ -62,25 +83,37 @@ def flatten_slots(slots_by_day: dict[str, list[dict[str, Any]]]) -> list[tuple[s return flattened -def first_day_with_slots(slots_by_day: dict[str, list[dict[str, Any]]], minimum_slots: int = 1) -> tuple[str, list[dict[str, Any]]]: +def first_day_with_slots( + slots_by_day: dict[str, list[dict[str, Any]]], minimum_slots: int = 1 +) -> tuple[str, list[dict[str, Any]]]: for day, slots in slots_by_day.items(): if len(slots) >= minimum_slots: return day, slots - raise AssertionError(f"expected at least one day with {minimum_slots} slot(s), got {slots_by_day}") + raise AssertionError( + f"expected at least one day with {minimum_slots} slot(s), got {slots_by_day}" + ) -def slot_window_to_iso(day_key: str, slot: dict[str, Any], timezone_name: str) -> tuple[str, str]: +def slot_window_to_iso( + day_key: str, slot: dict[str, Any], timezone_name: str +) -> tuple[str, str]: day_value = datetime.strptime(day_key, "%m/%d/%Y").date() timezone = ZoneInfo(timezone_name) start_hour, start_minute = [int(part) for part in slot["start_time"].split(":")] end_hour, end_minute = [int(part) for part in slot["end_time"].split(":")] - start_dt = datetime.combine(day_value, time(start_hour, start_minute), tzinfo=timezone) + start_dt = datetime.combine( + day_value, time(start_hour, start_minute), tzinfo=timezone + ) end_dt = datetime.combine(day_value, time(end_hour, end_minute), tzinfo=timezone) return start_dt.isoformat(), end_dt.isoformat() -def future_utc_window(days_from_now: int, start_hour: int = 17, duration_minutes: int = 30) -> tuple[str, str]: - start_dt = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0, hour=start_hour) +def future_utc_window( + days_from_now: int, start_hour: int = 17, duration_minutes: int = 30 +) -> tuple[str, str]: + start_dt = datetime.now(timezone.utc).replace( + microsecond=0, second=0, minute=0, hour=start_hour + ) start_dt = start_dt + timedelta(days=days_from_now) end_dt = start_dt + timedelta(minutes=duration_minutes) return start_dt.isoformat(), end_dt.isoformat() @@ -88,3 +121,287 @@ def future_utc_window(days_from_now: int, start_hour: int = 17, duration_minutes def unique_name(prefix: str) -> str: return f"{prefix}-{uuid4().hex[:8]}" + + +def create_public_user(api_client: ApiClient) -> dict[str, Any]: + response = api_client.post("/users/") + assert_json_response(response, 200) + return json_of(response)[0] + + +def public_slot_payload( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + minimum_slots: int = 1, + comments: str = "Public appointment", + citizen_name: str = "Codex Public User", + contact_information: str = "public@example.com", +) -> tuple[dict[str, Any], str, list[dict[str, Any]]]: + slots_response = api_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + assert_json_response(slots_response, 200) + day_key, slots = first_day_with_slots( + json_of(slots_response), minimum_slots=minimum_slots + ) + start_time, end_time = slot_window_to_iso( + day_key, slots[0], seeded_data["office_timezones"]["limited_office"] + ) + return ( + { + "service_id": seeded_data["service_ids"]["limited_office_service"], + "office_id": seeded_data["office_ids"]["limited_office"], + "start_time": start_time, + "end_time": end_time, + "comments": comments, + "citizen_name": citizen_name, + "contact_information": contact_information, + }, + day_key, + slots, + ) + + +def create_draft_appointment( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + minimum_slots: int = 1, +) -> dict[str, Any]: + payload, _day_key, _slots = public_slot_payload( + api_client, seeded_data, minimum_slots=minimum_slots + ) + response = api_client.post("/appointments/draft", json=payload) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + +def create_public_draft_and_payload( + api_client: ApiClient, seeded_data: dict[str, Any] +) -> tuple[dict[str, Any], dict[str, Any]]: + draft = create_draft_appointment(api_client, seeded_data, minimum_slots=1) + payload, _day_key, _slots = public_slot_payload( + api_client, seeded_data, minimum_slots=1 + ) + payload["appointment_draft_id"] = draft["appointment_id"] + return draft, payload + + +def create_internal_appointment( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + days_from_now: int, + recurring_uuid: Optional[str] = None, +) -> dict[str, Any]: + start_time, end_time = future_utc_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal appointment", + "citizen_name": unique_name("internal-appt"), + "contact_information": "internal@example.com", + "recurring_uuid": recurring_uuid, + }, + ) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + +def create_booking( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + days_from_now: int, + recurring_uuid: Optional[str] = None, + invigilator_ids: Optional[list[int]] = None, + office_id: Optional[int] = None, + room_id: Optional[int] = None, + booking_name: Optional[str] = None, +) -> dict[str, Any]: + start_time, end_time = future_utc_window(days_from_now, duration_minutes=120) + if invigilator_ids is None: + invigilator_ids = [seeded_data["invigilator_ids"][0]] + if office_id is None: + office_id = seeded_data["office_ids"]["test_office"] + if room_id is None: + room_id = seeded_data["room_id"] + if booking_name is None: + booking_name = unique_name("booking") + response = api_client.post( + "/bookings/", + json={ + "booking_name": booking_name, + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": office_id, + "room_id": room_id, + "start_time": start_time, + "end_time": end_time, + "recurring_uuid": recurring_uuid, + "invigilator_id": invigilator_ids, + }, + ) + assert_json_response(response, 201) + return json_of(response)["booking"] + + +def create_exam( + api_client: ApiClient, + seeded_data: dict[str, Any], + booking_id: Optional[int], + *, + event_id: str, + exam_type_id: Optional[int] = None, + office_id: Optional[int] = None, + **overrides: Any, +) -> dict[str, Any]: + expiry_date = ( + (datetime.now(timezone.utc) + timedelta(days=30)) + .replace(microsecond=0) + .isoformat() + ) + payload = { + "event_id": event_id, + "exam_method": "paper", + "exam_name": unique_name("exam"), + "exam_type_id": ( + seeded_data["exam_type_id"] if exam_type_id is None else exam_type_id + ), + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Codex exam notes", + "number_of_students": 19, + "office_id": ( + seeded_data["office_ids"]["test_office"] + if office_id is None + else office_id + ), + "offsite_location": "Test Office", + "expiry_date": expiry_date, + } + if booking_id is not None: + payload["booking_id"] = booking_id + payload.update(overrides) + response = api_client.post( + "/exams/", + json=payload, + ) + assert_json_response(response, 201) + return json_of(response)["exam"] + + +def create_citizen( + api_client: ApiClient, + position: int, + *, + name: str, + comments: Optional[str] = None, +) -> dict[str, Any]: + payload = {"citizen_name": name} + if comments is not None: + payload["citizen_comments"] = comments + + response = api_client.post(f"/citizens/{position}/add_citizen/", json=payload) + assert_json_response(response, 201) + return json_of(response)["citizen"] + + +def update_citizen(api_client: ApiClient, citizen_id: int, **payload) -> dict[str, Any]: + response = api_client.put(f"/citizens/{citizen_id}/", json=payload) + assert_json_response(response, 200) + return json_of(response)["citizen"] + + +def create_service_request( + api_client: ApiClient, + citizen_id: int, + *, + service_id: int, + channel_id: int, + quantity: int, +) -> dict[str, Any]: + response = api_client.post( + "/service_requests/", + json={ + "service_request": { + "citizen_id": citizen_id, + "service_id": service_id, + "channel_id": channel_id, + "quantity": quantity, + } + }, + ) + assert_json_response(response, 201) + return json_of(response)["service_request"] + + +def create_service_ready_citizen( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + position: int, + name: str, + service_id_key: str, + channel_id_key: str, + quantity: int, + counter_id_key: Optional[str] = None, + qt_xn_citizen_ind: Optional[int] = None, + comments: Optional[str] = None, +) -> tuple[dict[str, Any], dict[str, Any]]: + citizen = create_citizen(api_client, position, name=name, comments=comments) + + update_payload = {"citizen_name": name} + if comments is not None: + update_payload["citizen_comments"] = comments + if counter_id_key is not None: + update_payload["counter_id"] = seeded_data["counter_ids"][counter_id_key] + if qt_xn_citizen_ind is not None: + update_payload["qt_xn_citizen_ind"] = qt_xn_citizen_ind + + updated = update_citizen(api_client, citizen["citizen_id"], **update_payload) + service_request = create_service_request( + api_client, + updated["citizen_id"], + service_id=seeded_data["service_ids"][service_id_key], + channel_id=seeded_data["channel_ids"][channel_id_key], + quantity=quantity, + ) + return updated, service_request + + +def create_queue_ready_citizen( + api_client: ApiClient, + seeded_data: dict[str, Any], + *, + position: int, + name: str, + service_id_key: str, + channel_id_key: str, + quantity: int, + counter_id_key: str, + qt_xn_citizen_ind: int, + comments: Optional[str] = None, +) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]: + citizen, service_request = create_service_ready_citizen( + api_client, + seeded_data, + position=position, + name=name, + service_id_key=service_id_key, + channel_id_key=channel_id_key, + quantity=quantity, + counter_id_key=counter_id_key, + qt_xn_citizen_ind=qt_xn_citizen_ind, + comments=comments, + ) + queued_response = api_client.post( + f"/citizens/{citizen['citizen_id']}/add_to_queue/" + ) + assert_json_response(queued_response, 200) + return citizen, service_request, json_of(queued_response)["citizen"] diff --git a/api/app/tests/auth/__init__.py b/api/app/tests/auth/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/api/app/tests/auth/__init__.py @@ -0,0 +1 @@ + diff --git a/api/app/tests/auth/auth_support.py b/api/app/tests/auth/auth_support.py new file mode 100644 index 000000000..54234fb70 --- /dev/null +++ b/api/app/tests/auth/auth_support.py @@ -0,0 +1,271 @@ +from __future__ import annotations + +import io +import json +from datetime import datetime, timezone +from pathlib import Path +from uuid import uuid4 + +from app.tests.api_test_support import ( + assert_json_response, + create_booking, + create_exam, + create_internal_appointment, + create_public_user, + create_queue_ready_citizen, + create_service_ready_citizen, + create_service_request, + json_of, + public_slot_payload, +) + + +def configure_video_path( + app, monkeypatch, tmp_path: Path, *, office_number: int +) -> Path: + """Create a disposable video directory and point the app config at it.""" + video_dir = tmp_path / "videos" + video_dir.mkdir() + (video_dir / "sample.mp4").write_bytes(b"video-bytes") + (video_dir / "manifest.json").write_text( + json.dumps( + { + str(office_number): {"url": "https://example.com/office-video.mp4"}, + "default": {"url": "https://example.com/default-video.mp4"}, + } + ) + ) + monkeypatch.setitem(app.config, "VIDEO_PATH", str(video_dir)) + return video_dir + + +def create_public_appointment(public_client, seeded_data) -> dict: + """Create an appointment owned by the authenticated public test user.""" + create_public_user(public_client) + payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=1 + ) + response = public_client.post("/appointments/", json=payload) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + +def create_internal_exam_bundle(internal_ga_client, seeded_data) -> tuple[dict, dict]: + """Create a booking plus exam pair for exam-auth tests.""" + booking = create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = create_exam( + internal_ga_client, + seeded_data, + booking["booking_id"], + event_id=f"event-{uuid4().hex[:8]}", + ) + return booking, exam + + +def create_walkin_target(app, internal_ga_client, seeded_data) -> tuple[dict, str]: + """Create a queued citizen and persist a walk-in identifier for open walk-in routes.""" + citizen, _service_request, _queued = create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Walkin Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="Walkin citizen", + ) + walkin_id = f"walkin-{uuid4().hex[:8]}" + + with app.app_context(): + from app.models.theq import Citizen + from qsystem import db + + citizen_model = Citizen.find_citizen_by_id(citizen["citizen_id"]) + citizen_model.walkin_unique_id = walkin_id + citizen_model.notification_phone = "2505550100" + citizen_model.start_position = 1 + db.session.add(citizen_model) + db.session.commit() + + return citizen, walkin_id + + +def promote_internal_csr_to_support(app, *, username="cfms-postman-non-operator"): + """Promote a seeded internal CSR to SUPPORT for role-specific integration tests.""" + with app.app_context(): + from app.models.theq import CSR, Role + from qsystem import db + + csr = CSR.query.filter_by(username=username).first() + support_role = Role.query.filter_by(role_code="SUPPORT").first() + csr.role_id = support_role.role_id + db.session.add(csr) + db.session.commit() + return csr.csr_id + + +def patch_exam_integrations( + monkeypatch, + *, + create_individual_response=None, + create_group_response=None, + bulk_jobs=None, + transfer_response=None, + email_result=True, + download_job=None, + download_bytes=b"pdf-bytes", +): + """Replace BCMP and MinIO calls with deterministic local stubs.""" + from app.resources.bookings.exam.exam_bcmp import ExamBcmpPost + from app.resources.bookings.exam.exam_bulk_status import ExamList + from app.resources.bookings.exam.exam_download import ( + ExamStatus as ExamDownloadStatus, + ) + from app.resources.bookings.exam.exam_email_invigilator import ExamEmailInvigilator + from app.resources.bookings.exam.exam_transfer import ( + ExamStatus as ExamTransferStatus, + ) + from app.utilities.document_service import DocumentService + + if create_individual_response is None: + create_individual_response = {"jobId": "bcmp-job-123"} + if create_group_response is None: + create_group_response = {"jobId": "bcmp-group-job-123"} + if bulk_jobs is None: + bulk_jobs = [] + if transfer_response is None: + transfer_response = {"jobId": "transfer-job-123"} + if download_job is None: + download_job = { + "jobStatus": "PACKAGE_GENERATED", + "jobProperties": {"EXAM_PACKAGE_URL": "https://example.com/package.pdf"}, + } + + monkeypatch.setattr( + ExamBcmpPost.bcmp_service, + "create_individual_exam", + lambda *args, **kwargs: create_individual_response, + ) + monkeypatch.setattr( + ExamBcmpPost.bcmp_service, + "create_group_exam_bcmp", + lambda *args, **kwargs: create_group_response, + ) + monkeypatch.setattr( + ExamList.bcmp_service, + "bulk_check_exam_status", + lambda exams: {"jobs": list(bulk_jobs)}, + ) + monkeypatch.setattr( + ExamTransferStatus.bcmp_service, + "send_exam_to_bcmp", + lambda exam: transfer_response, + ) + monkeypatch.setattr( + ExamEmailInvigilator.bcmp_service, + "email_exam_invigilator", + lambda *args, **kwargs: email_result, + ) + monkeypatch.setattr( + ExamDownloadStatus.bcmp_service, + "check_exam_status", + lambda exam: download_job, + ) + monkeypatch.setattr( + DocumentService, + "get_presigned_put_url", + lambda self, name: "https://example.com/upload", + ) + + from app.resources.bookings.exam import exam_download as exam_download_module + + monkeypatch.setattr( + exam_download_module.urllib.request, + "urlopen", + lambda request: io.BytesIO(download_bytes), + ) + + +def build_bcmp_exam_payload(seeded_data) -> dict: + """Build a minimal BCMP exam payload that reaches the authenticated code path.""" + return { + "event_id": f"bcmp-{uuid4().hex[:8]}", + "exam_method": "paper", + "exam_name": "Environment", + "exam_type_id": seeded_data["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "BCMP Examinee", + "notes": "BCMP auth test", + "number_of_students": 1, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now(timezone.utc).replace(microsecond=0).isoformat(), + "is_pesticide": 1, + "sbc_managed": "sbc", + "ind_or_group": "individual", + "fees": "25.00", + } + + +def prepare_service_activation(api_client, seeded_data) -> tuple[dict, dict]: + """Create two service requests so the first one can be reactivated.""" + citizen, first_service = create_service_ready_citizen( + api_client, + seeded_data, + position=0, + name="Activation Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + create_service_request( + api_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + return citizen, first_service + + +def prepare_hold_or_finish_target(api_client, seeded_data) -> dict: + """Create a citizen and advance them to the Being Served state.""" + citizen, _service_request = create_service_ready_citizen( + api_client, + seeded_data, + position=0, + name="Serving Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + api_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + return citizen + + +def prepare_recurring_bookings(api_client, seeded_data) -> str: + """Create recurring bookings and return the shared recurring UUID.""" + recurring_uuid = str(uuid4()) + create_booking( + api_client, seeded_data, days_from_now=3, recurring_uuid=recurring_uuid + ) + create_booking( + api_client, seeded_data, days_from_now=4, recurring_uuid=recurring_uuid + ) + return recurring_uuid + + +def prepare_recurring_appointments(api_client, seeded_data) -> str: + """Create recurring appointments and return the shared recurring UUID.""" + recurring_uuid = str(uuid4()) + create_internal_appointment( + api_client, seeded_data, days_from_now=3, recurring_uuid=recurring_uuid + ) + create_internal_appointment( + api_client, seeded_data, days_from_now=4, recurring_uuid=recurring_uuid + ) + return recurring_uuid diff --git a/api/app/tests/auth/test_authenticated_only_routes.py b/api/app/tests/auth/test_authenticated_only_routes.py new file mode 100644 index 000000000..401a160b9 --- /dev/null +++ b/api/app/tests/auth/test_authenticated_only_routes.py @@ -0,0 +1,212 @@ +from __future__ import annotations + +from dataclasses import dataclass + +import pytest +from app.tests.api_test_support import ( + assert_not_auth_error, + assert_status, + assert_unauthorized, +) +from app.tests.auth.auth_support import ( + build_bcmp_exam_payload, + create_internal_exam_bundle, + patch_exam_integrations, +) + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +@dataclass(frozen=True) +class AuthenticatedRouteCase: + """Describe a route that requires authentication but uses additional in-method checks.""" + + id: str + method: str + build_request: object + success_assertion: object + public_forbidden: bool = False + + +def _assert_exact_status(expected_status: int): + def assertion(response): + assert_status(response, expected_status) + + return assertion + + +def _build_csr_states_request(ctx): + return {"path": "/csr_states/"} + + +def _build_citizens_request(ctx): + return {"path": "/citizens/"} + + +def _build_invigilators_offsite_request(ctx): + return {"path": "/invigilators/offsite/"} + + +def _build_exam_bcmp_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + return {"path": "/exams/bcmp/", "json": build_bcmp_exam_payload(ctx["seeded_data"])} + + +def _build_exam_bcmp_status_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + return {"path": "/exams/bcmp_status/", "json": {}} + + +def _build_exam_upload_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/exams/{exam['exam_id']}/upload/"} + + +def _build_exam_transfer_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/exams/{exam['exam_id']}/transfer/"} + + +def _build_exam_email_invigilator_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return { + "path": f"/exams/{exam['exam_id']}/email_invigilator/", + "json": { + "invigilator_id": ctx["seeded_data"]["invigilator_ids"][0], + "invigilator_name": "Homer Simpson", + "invigilator_email": "homer@example.com", + "invigilator_phone": "2505550100", + }, + } + + +def _build_exam_download_request(ctx): + patch_exam_integrations(ctx["monkeypatch"]) + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/exams/{exam['exam_id']}/download/"} + + +AUTHENTICATED_ONLY_CASES = [ + AuthenticatedRouteCase( + "GET /csr_states/", + "GET", + _build_csr_states_request, + _assert_exact_status(200), + public_forbidden=True, + ), + AuthenticatedRouteCase( + "GET /citizens/", + "GET", + _build_citizens_request, + _assert_exact_status(200), + public_forbidden=True, + ), + AuthenticatedRouteCase( + "GET /invigilators/offsite/", + "GET", + _build_invigilators_offsite_request, + _assert_exact_status(200), + ), + AuthenticatedRouteCase( + "POST /exams/bcmp/", "POST", _build_exam_bcmp_request, assert_not_auth_error + ), + AuthenticatedRouteCase( + "POST /exams/bcmp_status/", + "POST", + _build_exam_bcmp_status_request, + _assert_exact_status(200), + ), + AuthenticatedRouteCase( + "GET /exams//upload/", + "GET", + _build_exam_upload_request, + _assert_exact_status(200), + ), + AuthenticatedRouteCase( + "POST /exams//transfer/", + "POST", + _build_exam_transfer_request, + _assert_exact_status(202), + ), + AuthenticatedRouteCase( + "POST /exams//email_invigilator/", + "POST", + _build_exam_email_invigilator_request, + _assert_exact_status(200), + ), + AuthenticatedRouteCase( + "GET /exams//download/", + "GET", + _build_exam_download_request, + _assert_exact_status(200), + ), +] + + +def _context(internal_ga_client, monkeypatch, public_client, seeded_data): + return { + "internal_ga_client": internal_ga_client, + "monkeypatch": monkeypatch, + "public_client": public_client, + "seeded_data": seeded_data, + } + + +@pytest.mark.parametrize("case", AUTHENTICATED_ONLY_CASES, ids=lambda case: case.id) +def test_bare_client_receives_401_for_authenticated_only_routes( + bare_client, case, internal_ga_client, monkeypatch, public_client, seeded_data +): + """Assert that authenticated-only routes reject missing identities before any in-method checks run.""" + ctx = _context(internal_ga_client, monkeypatch, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = bare_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_unauthorized(response) + + +@pytest.mark.parametrize("case", AUTHENTICATED_ONLY_CASES, ids=lambda case: case.id) +def test_internal_user_reaches_non_auth_behavior_for_authenticated_only_routes( + bare_client, case, internal_ga_client, monkeypatch, public_client, seeded_data +): + """Assert that authenticated internal users still reach the non-auth behavior for authenticated-only routes.""" + ctx = _context(internal_ga_client, monkeypatch, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = internal_ga_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + case.success_assertion(response) + + +@pytest.mark.parametrize( + "case", + [case for case in AUTHENTICATED_ONLY_CASES if case.public_forbidden], + ids=lambda case: case.id, +) +def test_public_user_receives_403_when_authenticated_only_routes_perform_explicit_role_checks( + bare_client, case, internal_ga_client, monkeypatch, public_client, seeded_data +): + """Assert that public users receive 403 only on authenticated-only routes that explicitly enforce internal roles.""" + ctx = _context(internal_ga_client, monkeypatch, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = public_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_status(response, 403) diff --git a/api/app/tests/auth/test_cookie_routes.py b/api/app/tests/auth/test_cookie_routes.py new file mode 100644 index 000000000..30443fa5a --- /dev/null +++ b/api/app/tests/auth/test_cookie_routes.py @@ -0,0 +1,19 @@ +import pytest +from app.tests.api_test_support import assert_unauthorized + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +def test_bare_client_receives_401_for_cookie_authenticated_login(bare_client): + """Assert that the admin login route still rejects unauthenticated cookie requests.""" + response = bare_client.get("/login/") + + assert_unauthorized(response) + + +def test_internal_user_can_reach_cookie_authenticated_login(internal_ga_client): + """Assert that an authenticated internal user still reaches the admin login redirect.""" + response = internal_ga_client.get("/login/") + + assert response.status_code == 302, response.get_data(as_text=True) + assert response.headers["Location"].endswith("/admin/") diff --git a/api/app/tests/auth/test_internal_only_routes.py b/api/app/tests/auth/test_internal_only_routes.py new file mode 100644 index 000000000..2f9f103c7 --- /dev/null +++ b/api/app/tests/auth/test_internal_only_routes.py @@ -0,0 +1,878 @@ +from __future__ import annotations + +import io +from dataclasses import dataclass +from datetime import datetime, timezone +from uuid import uuid4 +from zoneinfo import ZoneInfo + +import pytest +from app.tests.api_test_support import ( + assert_forbidden, + assert_not_auth_error, + assert_status, + assert_unauthorized, + create_booking, + create_citizen, + create_internal_appointment, + create_queue_ready_citizen, + create_service_ready_citizen, + create_service_request, + future_utc_window, + json_of, +) +from app.tests.auth.auth_support import ( + configure_video_path, + create_internal_exam_bundle, + create_walkin_target, + prepare_hold_or_finish_target, + prepare_recurring_appointments, + prepare_recurring_bookings, + prepare_service_activation, +) + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +@dataclass(frozen=True) +class InternalRouteCase: + """Describe an internal-only endpoint and how to reach its non-authenticated success path.""" + + id: str + method: str + build_request: object + success_assertion: object + + +def _assert_exact_status(expected_status: int): + def assertion(response): + assert_status(response, expected_status) + + return assertion + + +def _build_context( + app, + bare_client, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, +): + return { + "app": app, + "bare_client": bare_client, + "internal_ga_client": internal_ga_client, + "monkeypatch": monkeypatch, + "public_client": public_client, + "seeded_data": seeded_data, + "tmp_path": tmp_path, + } + + +def _build_update_csr_request(ctx): + csr_id = ctx["seeded_data"]["csr_ids"]["ga"] + return {"path": f"/csrs/{csr_id}/", "json": {"receptionist_ind": 1}} + + +def _build_update_service_request(ctx): + citizen, service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Service Request Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return { + "path": f"/service_requests/{service_request['sr_id']}/", + "json": {"quantity": 2}, + } + + +def _build_activate_service_request(ctx): + _citizen, first_service = prepare_service_activation( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/service_requests/{first_service['sr_id']}/activate/"} + + +def _build_channels_request(ctx): + return {"path": "/channels/"} + + +def _build_create_service_request(ctx): + citizen = create_citizen( + ctx["internal_ga_client"], 0, name="Create Service Citizen" + ) + return { + "path": "/service_requests/", + "json": { + "service_request": { + "citizen_id": citizen["citizen_id"], + "service_id": ctx["seeded_data"]["service_ids"]["ptax"], + "channel_id": ctx["seeded_data"]["channel_ids"]["phone"], + "quantity": 1, + } + }, + } + + +def _build_csrs_request(ctx): + return {"path": "/csrs/"} + + +def _build_csr_me_request(ctx): + return {"path": "/csrs/me/"} + + +def _build_video_list_request(ctx): + configure_video_path( + ctx["app"], + ctx["monkeypatch"], + ctx["tmp_path"], + office_number=ctx["seeded_data"]["office_numbers"]["test_office"], + ) + return {"path": "/videofiles/"} + + +def _build_video_delete_request(ctx): + configure_video_path( + ctx["app"], + ctx["monkeypatch"], + ctx["tmp_path"], + office_number=ctx["seeded_data"]["office_numbers"]["test_office"], + ) + return {"path": "/videofiles/", "json": {"name": "sample.mp4"}} + + +def _build_upload_request(ctx): + configure_video_path( + ctx["app"], + ctx["monkeypatch"], + ctx["tmp_path"], + office_number=ctx["seeded_data"]["office_numbers"]["test_office"], + ) + return { + "path": "/upload/", + "data": { + "manifest": '{"default": {"url": "https://example.com/video.mp4"}}', + "file": (io.BytesIO(b"video-bytes"), "sample.mp4"), + }, + "content_type": "multipart/form-data", + } + + +def _build_refresh_services_request(ctx): + return { + "path": f"/services/refresh/?office_id={ctx['seeded_data']['office_ids']['test_office']}" + } + + +def _build_remove_from_queue_request(ctx): + citizen, _service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Remove From Queue Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return {"path": f"/citizens/{citizen['citizen_id']}/remove_from_queue/"} + + +def _build_begin_service_request(ctx): + citizen, _service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Begin Service Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return {"path": f"/citizens/{citizen['citizen_id']}/begin_service/"} + + +def _build_citizen_service_requests_request(ctx): + citizen, _service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Citizen Services Lookup", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return {"path": f"/citizens/{citizen['citizen_id']}/service_requests/"} + + +def _build_citizen_detail_request(ctx): + citizen = create_citizen(ctx["internal_ga_client"], 0, name="Citizen Detail") + return {"path": f"/citizens/{citizen['citizen_id']}/"} + + +def _build_citizen_update_request(ctx): + citizen = create_citizen(ctx["internal_ga_client"], 0, name="Citizen Update") + return { + "path": f"/citizens/{citizen['citizen_id']}/", + "json": {"citizen_name": "Citizen Updated"}, + } + + +def _build_specific_invite_request(ctx): + citizen, _service_request, _queued = create_queue_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Specific Invite Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + return {"path": f"/citizens/{citizen['citizen_id']}/invite/"} + + +def _build_citizen_left_request(ctx): + citizen, _service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Citizen Left", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return {"path": f"/citizens/{citizen['citizen_id']}/citizen_left/"} + + +def _build_add_to_queue_request(ctx): + citizen, _service_request = create_service_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Add To Queue", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + return {"path": f"/citizens/{citizen['citizen_id']}/add_to_queue/"} + + +def _build_finish_service_request(ctx): + citizen = prepare_hold_or_finish_target( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/citizens/{citizen['citizen_id']}/finish_service/"} + + +def _build_place_on_hold_request(ctx): + citizen = prepare_hold_or_finish_target( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/citizens/{citizen['citizen_id']}/place_on_hold/"} + + +def _build_add_citizen_request(ctx): + return { + "path": "/citizens/0/add_citizen/", + "json": {"citizen_name": "Added Citizen"}, + } + + +def _build_generic_invite_request(ctx): + create_queue_ready_citizen( + ctx["internal_ga_client"], + ctx["seeded_data"], + position=0, + name="Generic Invite Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + return { + "path": "/citizens/invite/", + "json": {"counter_id": ctx["seeded_data"]["counter_ids"]["counter"]}, + } + + +def _build_rooms_request(ctx): + return {"path": "/rooms/"} + + +def _build_booking_detail_request(ctx): + booking = create_booking( + ctx["internal_ga_client"], ctx["seeded_data"], days_from_now=2 + ) + return {"path": f"/bookings/{booking['booking_id']}/"} + + +def _build_recurring_booking_update_request(ctx): + recurring_uuid = prepare_recurring_bookings( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return { + "path": f"/bookings/recurring/{recurring_uuid}", + "json": {"booking_name": "Recurring Booking Updated"}, + } + + +def _build_recurring_booking_current_office_delete_request(ctx): + recurring_uuid = prepare_recurring_bookings( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/bookings/recurring/current-office/{recurring_uuid}"} + + +def _build_recurring_booking_stat_delete_request(ctx): + recurring_uuid = prepare_recurring_bookings( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/bookings/recurring/stat/{recurring_uuid}"} + + +def _build_booking_create_request(ctx): + start_time, end_time = future_utc_window(2, duration_minutes=120) + return { + "path": "/bookings/", + "json": { + "booking_name": f"Auth Booking {uuid4().hex[:6]}", + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": ctx["seeded_data"]["office_ids"]["test_office"], + "room_id": ctx["seeded_data"]["room_id"], + "start_time": start_time, + "end_time": end_time, + "invigilator_id": [ctx["seeded_data"]["invigilator_ids"][0]], + }, + } + + +def _build_invigilator_update_request(ctx): + invigilator_id = ctx["seeded_data"]["invigilator_ids"][0] + return {"path": f"/invigilator/{invigilator_id}/?subtract=True&add=False"} + + +def _build_invigilator_list_request(ctx): + return {"path": "/invigilators/"} + + +def _build_booking_delete_request(ctx): + booking = create_booking( + ctx["internal_ga_client"], ctx["seeded_data"], days_from_now=2 + ) + return {"path": f"/bookings/{booking['booking_id']}/"} + + +def _build_booking_list_request(ctx): + return {"path": "/bookings/"} + + +def _build_booking_recurring_delete_request(ctx): + recurring_uuid = prepare_recurring_bookings( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/bookings/recurring/{recurring_uuid}"} + + +def _build_walkin_reminder_request(ctx): + citizen, _walkin_id = create_walkin_target( + ctx["app"], ctx["internal_ga_client"], ctx["seeded_data"] + ) + return { + "path": "/send-reminder/line-walkin/", + "json": {"previous_citizen_id": citizen["citizen_id"]}, + } + + +def _build_appointment_recurring_update_request(ctx): + recurring_uuid = prepare_recurring_appointments( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return { + "path": f"/appointments/recurring/{recurring_uuid}", + "json": {"comments": "Recurring appointment updated"}, + } + + +def _build_appointment_all_stat_delete_request(ctx): + recurring_uuid = prepare_recurring_appointments( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/appointments/all-stat/{recurring_uuid}"} + + +def _build_appointment_list_request(ctx): + return {"path": "/appointments/"} + + +def _build_appointment_recurring_delete_request(ctx): + recurring_uuid = prepare_recurring_appointments( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/appointments/recurring/{recurring_uuid}"} + + +def _build_appointment_detail_request(ctx): + appointment = create_internal_appointment( + ctx["internal_ga_client"], ctx["seeded_data"], days_from_now=2 + ) + return {"path": f"/appointments/{appointment['appointment_id']}/"} + + +def _build_exam_create_request(ctx): + booking = create_booking( + ctx["internal_ga_client"], ctx["seeded_data"], days_from_now=5 + ) + return { + "path": "/exams/", + "json": { + "booking_id": booking["booking_id"], + "event_id": f"auth-event-{uuid4().hex[:6]}", + "exam_method": "paper", + "exam_name": "Auth Exam", + "exam_type_id": ctx["seeded_data"]["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "Auth Examinee", + "notes": "Auth exam notes", + "number_of_students": 19, + "office_id": ctx["seeded_data"]["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now(timezone.utc) + .replace(microsecond=0) + .isoformat(), + }, + } + + +def _build_exam_type_list_request(ctx): + return {"path": "/exam_types/"} + + +def _build_exam_update_request(ctx): + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return { + "path": f"/exams/{exam['exam_id']}/", + "json": {"exam_name": "Updated Auth Exam"}, + } + + +def _build_exam_detail_request(ctx): + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/exams/{exam['exam_id']}/"} + + +def _build_exam_list_request(ctx): + return {"path": "/exams/"} + + +def _build_exam_export_request(ctx): + booking, _exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + export_date = ( + datetime.fromisoformat(booking["start_time"].replace("Z", "+00:00")) + .astimezone(ZoneInfo(ctx["seeded_data"]["office_timezones"]["test_office"])) + .date() + ) + return { + "path": f"/exams/export/?start_date={export_date.isoformat()}&end_date={export_date.isoformat()}" + } + + +def _build_exam_delete_request(ctx): + _booking, exam = create_internal_exam_bundle( + ctx["internal_ga_client"], ctx["seeded_data"] + ) + return {"path": f"/exams/{exam['exam_id']}/"} + + +def _build_exam_event_lookup_request(ctx): + booking = create_booking( + ctx["internal_ga_client"], ctx["seeded_data"], days_from_now=5 + ) + event_id = 9000 + booking["booking_id"] + create_exam_response = ctx["internal_ga_client"].post( + "/exams/", + json={ + "booking_id": booking["booking_id"], + "event_id": str(event_id), + "exam_method": "paper", + "exam_name": "Lookup Exam", + "exam_type_id": ctx["seeded_data"]["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "Lookup Examinee", + "notes": "Lookup exam notes", + "number_of_students": 19, + "office_id": ctx["seeded_data"]["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now(timezone.utc) + .replace(microsecond=0) + .isoformat(), + }, + ) + assert_status(create_exam_response, 201) + return {"path": f"/exams/event_id/{event_id}/"} + + +INTERNAL_ONLY_CASES = [ + InternalRouteCase( + "PUT /csrs//", "PUT", _build_update_csr_request, _assert_exact_status(200) + ), + InternalRouteCase( + "PUT /service_requests//", + "PUT", + _build_update_service_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /service_requests//activate/", + "POST", + _build_activate_service_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "GET /channels/", "GET", _build_channels_request, _assert_exact_status(200) + ), + InternalRouteCase( + "POST /service_requests/", + "POST", + _build_create_service_request, + _assert_exact_status(201), + ), + InternalRouteCase( + "GET /csrs/", "GET", _build_csrs_request, _assert_exact_status(200) + ), + InternalRouteCase( + "GET /csrs/me/", "GET", _build_csr_me_request, _assert_exact_status(200) + ), + InternalRouteCase( + "GET /videofiles/", "GET", _build_video_list_request, _assert_exact_status(200) + ), + InternalRouteCase( + "DELETE /videofiles/", + "DELETE", + _build_video_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "POST /upload/", "POST", _build_upload_request, assert_not_auth_error + ), + InternalRouteCase( + "GET /services/refresh/", + "GET", + _build_refresh_services_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//remove_from_queue/", + "POST", + _build_remove_from_queue_request, + assert_not_auth_error, + ), + InternalRouteCase( + "POST /citizens//begin_service/", + "POST", + _build_begin_service_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "GET /citizens//service_requests/", + "GET", + _build_citizen_service_requests_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "GET /citizens//", + "GET", + _build_citizen_detail_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "PUT /citizens//", + "PUT", + _build_citizen_update_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//invite/", + "POST", + _build_specific_invite_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//citizen_left/", + "POST", + _build_citizen_left_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//add_to_queue/", + "POST", + _build_add_to_queue_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//finish_service/", + "POST", + _build_finish_service_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//place_on_hold/", + "POST", + _build_place_on_hold_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /citizens//add_citizen/", + "POST", + _build_add_citizen_request, + _assert_exact_status(201), + ), + InternalRouteCase( + "POST /citizens/invite/", + "POST", + _build_generic_invite_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "GET /rooms/", "GET", _build_rooms_request, _assert_exact_status(200) + ), + InternalRouteCase( + "GET /bookings//", + "GET", + _build_booking_detail_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "PUT /bookings/recurring/", + "PUT", + _build_recurring_booking_update_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "DELETE /bookings/recurring/current-office/", + "DELETE", + _build_recurring_booking_current_office_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "DELETE /bookings/recurring/stat/", + "DELETE", + _build_recurring_booking_stat_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "POST /bookings/", + "POST", + _build_booking_create_request, + _assert_exact_status(201), + ), + InternalRouteCase( + "PUT /invigilator//", + "PUT", + _build_invigilator_update_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "GET /invigilators/", + "GET", + _build_invigilator_list_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "DELETE /bookings//", + "DELETE", + _build_booking_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "GET /bookings/", "GET", _build_booking_list_request, _assert_exact_status(200) + ), + InternalRouteCase( + "DELETE /bookings/recurring/", + "DELETE", + _build_booking_recurring_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "POST /send-reminder/line-walkin/", + "POST", + _build_walkin_reminder_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "PUT /appointments/recurring/", + "PUT", + _build_appointment_recurring_update_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "DELETE /appointments/all-stat/", + "DELETE", + _build_appointment_all_stat_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "GET /appointments/", + "GET", + _build_appointment_list_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "DELETE /appointments/recurring/", + "DELETE", + _build_appointment_recurring_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "GET /appointments//", + "GET", + _build_appointment_detail_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "POST /exams/", "POST", _build_exam_create_request, _assert_exact_status(201) + ), + InternalRouteCase( + "GET /exam_types/", + "GET", + _build_exam_type_list_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "PUT /exams//", "PUT", _build_exam_update_request, _assert_exact_status(201) + ), + InternalRouteCase( + "GET /exams//", "GET", _build_exam_detail_request, _assert_exact_status(200) + ), + InternalRouteCase( + "GET /exams/", "GET", _build_exam_list_request, _assert_exact_status(200) + ), + InternalRouteCase( + "GET /exams/export/", + "GET", + _build_exam_export_request, + _assert_exact_status(200), + ), + InternalRouteCase( + "DELETE /exams//", + "DELETE", + _build_exam_delete_request, + _assert_exact_status(204), + ), + InternalRouteCase( + "GET /exams/event_id//", + "GET", + _build_exam_event_lookup_request, + _assert_exact_status(200), + ), +] + + +@pytest.mark.parametrize("case", INTERNAL_ONLY_CASES, ids=lambda case: case.id) +def test_bare_client_receives_401_for_internal_only_routes( + app, + bare_client, + case, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, +): + """Assert that internal-only routes reject unauthenticated requests.""" + ctx = _build_context( + app, + bare_client, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, + ) + request_kwargs = case.build_request(ctx) + + response = bare_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_unauthorized(response) + + +@pytest.mark.parametrize("case", INTERNAL_ONLY_CASES, ids=lambda case: case.id) +def test_public_user_receives_403_for_internal_only_routes( + app, + bare_client, + case, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, +): + """Assert that public users remain forbidden from internal-only routes.""" + ctx = _build_context( + app, + bare_client, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, + ) + request_kwargs = case.build_request(ctx) + + response = public_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_forbidden(response) + + +@pytest.mark.parametrize("case", INTERNAL_ONLY_CASES, ids=lambda case: case.id) +def test_internal_user_reaches_non_auth_behavior_for_internal_only_routes( + app, + bare_client, + case, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, +): + """Assert that internal users still reach each internal-only route's non-auth behavior.""" + ctx = _build_context( + app, + bare_client, + internal_ga_client, + monkeypatch, + public_client, + seeded_data, + tmp_path, + ) + request_kwargs = case.build_request(ctx) + + response = internal_ga_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + case.success_assertion(response) diff --git a/api/app/tests/auth/test_mixed_role_routes.py b/api/app/tests/auth/test_mixed_role_routes.py new file mode 100644 index 000000000..7842d0e01 --- /dev/null +++ b/api/app/tests/auth/test_mixed_role_routes.py @@ -0,0 +1,152 @@ +import pytest +from app.tests.api_test_support import ( + assert_json_response, + assert_unauthorized, + create_internal_appointment, + create_public_user, + json_of, + public_slot_payload, + slot_window_to_iso, +) +from app.tests.auth.auth_support import create_public_appointment + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +def test_bare_client_receives_401_for_mixed_role_appointment_create( + bare_client, seeded_data +): + """Assert that appointment creation still requires some authenticated role even though multiple roles are allowed.""" + payload, _day_key, _slots = public_slot_payload( + bare_client, seeded_data, minimum_slots=1 + ) + response = bare_client.post("/appointments/", json=payload) + + assert_unauthorized(response) + + +def test_bare_client_receives_401_for_mixed_role_appointment_update( + bare_client, internal_ga_client, seeded_data +): + """Assert that appointment updates still reject unauthenticated callers.""" + appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + response = bare_client.put( + f"/appointments/{appointment['appointment_id']}/", json={"comments": "No auth"} + ) + + assert_unauthorized(response) + + +def test_bare_client_receives_401_for_mixed_role_appointment_delete( + bare_client, internal_ga_client, seeded_data +): + """Assert that appointment deletion still rejects unauthenticated callers.""" + appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + response = bare_client.delete(f"/appointments/{appointment['appointment_id']}/") + + assert_unauthorized(response) + + +def test_internal_user_can_create_appointments_on_mixed_role_route( + internal_ga_client, seeded_data +): + """Assert that internal users can still create appointments on the shared appointment route.""" + appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + assert appointment["appointment_id"] > 0 + + +def test_internal_user_can_update_appointments_on_mixed_role_route( + internal_ga_client, seeded_data +): + """Assert that internal users can still update appointments on the shared appointment route.""" + appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.put( + f"/appointments/{appointment['appointment_id']}/", + json={"comments": "Internal mixed-route update"}, + ) + + assert_json_response(response, 200) + assert json_of(response)["appointment"]["comments"] == "Internal mixed-route update" + + +def test_internal_user_can_delete_appointments_on_mixed_role_route( + internal_ga_client, seeded_data +): + """Assert that internal users can still delete appointments on the shared appointment route.""" + appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.delete( + f"/appointments/{appointment['appointment_id']}/" + ) + + assert response.status_code == 204, response.get_data(as_text=True) + + +def test_public_user_can_create_appointments_on_mixed_role_route( + public_client, seeded_data +): + """Assert that public users can still create appointments on the shared appointment route.""" + create_public_user(public_client) + payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=1 + ) + + response = public_client.post("/appointments/", json=payload) + + assert_json_response(response, 201) + assert json_of(response)["appointment"]["online_flag"] is True + + +def test_public_user_can_update_their_own_appointments_on_mixed_role_route( + public_client, seeded_data +): + """Assert that public users can still update their own appointments on the shared appointment route.""" + create_public_user(public_client) + payload, day_key, slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + create_response = public_client.post("/appointments/", json=payload) + assert_json_response(create_response, 201) + appointment = json_of(create_response)["appointment"] + updated_start_time, updated_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + + response = public_client.put( + f"/appointments/{appointment['appointment_id']}/", + json={ + "comments": "Public mixed-route update", + "office_id": appointment["office_id"], + "service_id": appointment["service_id"], + "start_time": updated_start_time, + "end_time": updated_end_time, + }, + ) + + assert_json_response(response, 200) + assert json_of(response)["appointment"]["comments"] == "Public mixed-route update" + + +def test_public_user_can_delete_their_own_appointments_on_mixed_role_route( + public_client, seeded_data +): + """Assert that public users can still delete their own appointments on the shared appointment route.""" + appointment = create_public_appointment(public_client, seeded_data) + + response = public_client.delete(f"/appointments/{appointment['appointment_id']}/") + + assert response.status_code == 204, response.get_data(as_text=True) diff --git a/api/app/tests/auth/test_open_routes.py b/api/app/tests/auth/test_open_routes.py new file mode 100644 index 000000000..4bc6c5867 --- /dev/null +++ b/api/app/tests/auth/test_open_routes.py @@ -0,0 +1,171 @@ +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_draft_appointment, + json_of, + public_slot_payload, +) +from app.tests.auth.auth_support import configure_video_path, create_walkin_target + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +@pytest.mark.parametrize( + ("path", "query", "expected_key"), + [ + pytest.param("/healthz/", "", "message", id="GET /healthz/"), + pytest.param("/readyz/", "", "message", id="GET /readyz/"), + pytest.param("/offices/", "", "offices", id="GET /offices/"), + pytest.param("/services/", "", "services", id="GET /services/"), + pytest.param("/categories/", "", "categories", id="GET /categories/"), + ], +) +def test_bare_client_can_access_simple_open_routes( + bare_client, path, query, expected_key +): + """Assert that explicitly open routes remain reachable without an authenticated identity.""" + response = bare_client.get(f"{path}{query}") + + assert_json_response(response, 200) + assert expected_key in json_of(response) + + +def test_bare_client_can_access_logout_without_authentication(bare_client): + """Assert that logout remains reachable without an authenticated identity.""" + response = bare_client.get("/logout/") + + assert response.status_code == 302, response.get_data(as_text=True) + assert response.headers["Location"].endswith("/admin/") + + +def test_bare_client_can_access_smartboard_without_authentication( + bare_client, seeded_data +): + """Assert that the public smartboard endpoint remains reachable without authentication.""" + office_number = seeded_data["office_numbers"]["test_office"] + response = bare_client.get(f"/smartboard/?office_number={office_number}") + + assert_json_response(response, 200) + body = json_of(response) + assert "office_type" in body + assert "citizens" in body + + +def test_bare_client_can_access_smartboard_side_menu_without_authentication( + bare_client, seeded_data +): + """Assert that the smartboard side-menu endpoint remains publicly reachable.""" + office_number = seeded_data["office_numbers"]["test_office"] + response = bare_client.get(f"/smardboard/side-menu/{office_number}") + + assert_json_response(response, 200) + assert "office" in json_of(response) + + +def test_bare_client_can_access_public_video_manifest_without_authentication( + bare_client, app, monkeypatch, seeded_data, tmp_path +): + """Assert that public video lookup remains reachable without an authenticated identity.""" + office_number = seeded_data["office_numbers"]["test_office"] + configure_video_path(app, monkeypatch, tmp_path, office_number=office_number) + + response = bare_client.get(f"/videofiles/{office_number}") + + assert_json_response(response, 200) + assert json_of(response)["videourl"].endswith(".mp4") + + +def test_bare_client_can_access_slots_without_authentication(bare_client, seeded_data): + """Assert that appointment slot discovery remains publicly reachable.""" + response = bare_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + + assert_json_response(response, 200) + assert json_of(response) + + +def test_bare_client_can_access_walkin_lookup_without_authentication( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that walk-in queue lookups remain publicly reachable without authentication.""" + _citizen, walkin_id = create_walkin_target(app, internal_ga_client, seeded_data) + + response = bare_client.get(f"/citizen/all-walkin/{walkin_id}/") + + assert_json_response(response, 200) + assert "citizen" in json_of(response) + + +def test_bare_client_can_access_waiting_queue_details_without_authentication( + bare_client, seeded_data, app +): + """Assert that waiting smartboard queue details remain publicly reachable.""" + office_number = seeded_data["office_numbers"]["test_office"] + + with app.app_context(): + from app.models.theq import Office + from qsystem import db + + office = Office.find_by_id(seeded_data["office_ids"]["test_office"]) + office.currently_waiting = 0 + db.session.add(office) + db.session.commit() + + response = bare_client.get(f"/smardboard/Q-details/waiting/{office_number}") + + assert_json_response(response, 200) + assert "citizen_in_q" in json_of(response) + + +def test_bare_client_can_access_upcoming_queue_details_without_authentication( + bare_client, seeded_data, app +): + """Assert that upcoming smartboard queue details remain publicly reachable.""" + office_number = seeded_data["office_numbers"]["test_office"] + + with app.app_context(): + from app.models.theq import Office + from qsystem import db + + office = Office.find_by_id(seeded_data["office_ids"]["test_office"]) + office.currently_waiting = 0 + db.session.add(office) + db.session.commit() + + response = bare_client.get(f"/smardboard/Q-details/upcoming/{office_number}") + + assert_json_response(response, 200) + assert "booked_not_checkin" in json_of(response) + + +def test_bare_client_can_create_a_draft_appointment_without_authentication( + bare_client, seeded_data +): + """Assert that draft appointments remain creatable before a user authenticates.""" + payload, _day_key, _slots = public_slot_payload( + bare_client, seeded_data, minimum_slots=1 + ) + response = bare_client.post("/appointments/draft", json=payload) + + assert_json_response(response, 201) + assert json_of(response)["appointment"]["is_draft"] is True + + +def test_bare_client_can_delete_a_draft_appointment_without_authentication( + bare_client, seeded_data +): + """Assert that draft appointments remain deletable without an authenticated identity.""" + draft = create_draft_appointment(bare_client, seeded_data) + + response = bare_client.delete(f"/appointments/draft/{draft['appointment_id']}/") + + assert response.status_code == 204, response.get_data(as_text=True) + + +def test_bare_client_can_flush_expired_drafts_without_authentication(bare_client): + """Assert that draft flushing remains callable without an authenticated identity.""" + response = bare_client.post("/appointments/draft/flush") + + assert_json_response(response, 200) + assert "deleted_draft_ids" in json_of(response) diff --git a/api/app/tests/auth/test_public_user_routes.py b/api/app/tests/auth/test_public_user_routes.py new file mode 100644 index 000000000..1081c5dac --- /dev/null +++ b/api/app/tests/auth/test_public_user_routes.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +from dataclasses import dataclass + +import pytest +from app.tests.api_test_support import ( + assert_forbidden, + assert_status, + assert_unauthorized, + create_public_user, + json_of, + public_slot_payload, +) + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +@dataclass(frozen=True) +class PublicRouteCase: + """Describe a public-user-only route and the request needed to reach it.""" + + id: str + method: str + build_request: object + expected_status: int + + +def _create_user_request(ctx): + return {"path": "/users/"} + + +def _update_user_request(ctx): + user = create_public_user(ctx["public_client"]) + return { + "path": f"/users/{user['user_id']}/", + "json": { + "email": "updated-public@example.com", + "telephone": "2505550100", + "send_email_reminders": True, + "send_sms_reminders": True, + }, + } + + +def _me_request(ctx): + create_public_user(ctx["public_client"]) + return {"path": "/users/me/"} + + +def _appointments_request(ctx): + create_public_user(ctx["public_client"]) + payload, _day_key, _slots = public_slot_payload( + ctx["public_client"], ctx["seeded_data"], minimum_slots=1 + ) + create_response = ctx["public_client"].post("/appointments/", json=payload) + assert_status(create_response, 201) + return {"path": "/users/appointments/"} + + +PUBLIC_ROUTE_CASES = [ + PublicRouteCase("POST /users/", "POST", _create_user_request, 200), + PublicRouteCase("PUT /users//", "PUT", _update_user_request, 200), + PublicRouteCase("GET /users/me/", "GET", _me_request, 200), + PublicRouteCase("GET /users/appointments/", "GET", _appointments_request, 200), +] + + +def _context(bare_client, internal_ga_client, public_client, seeded_data): + return { + "bare_client": bare_client, + "internal_ga_client": internal_ga_client, + "public_client": public_client, + "seeded_data": seeded_data, + } + + +@pytest.mark.parametrize("case", PUBLIC_ROUTE_CASES, ids=lambda case: case.id) +def test_bare_client_receives_401_for_public_user_routes( + bare_client, case, internal_ga_client, public_client, seeded_data +): + """Assert that public-user routes still require an authenticated public identity.""" + ctx = _context(bare_client, internal_ga_client, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = bare_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_unauthorized(response) + + +@pytest.mark.parametrize("case", PUBLIC_ROUTE_CASES, ids=lambda case: case.id) +def test_internal_user_receives_403_for_public_user_routes( + bare_client, case, internal_ga_client, public_client, seeded_data +): + """Assert that internal users remain forbidden from public-user-only routes.""" + ctx = _context(bare_client, internal_ga_client, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = internal_ga_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_forbidden(response) + + +@pytest.mark.parametrize("case", PUBLIC_ROUTE_CASES, ids=lambda case: case.id) +def test_public_user_can_reach_public_user_routes( + bare_client, case, internal_ga_client, public_client, seeded_data +): + """Assert that authenticated public users still reach their user-management routes.""" + ctx = _context(bare_client, internal_ga_client, public_client, seeded_data) + request_kwargs = case.build_request(ctx) + + response = public_client.open( + request_kwargs.pop("path"), method=case.method, **request_kwargs + ) + + assert_status(response, case.expected_status) + assert response.get_json() is not None diff --git a/api/app/tests/auth/test_reminder_job_routes.py b/api/app/tests/auth/test_reminder_job_routes.py new file mode 100644 index 000000000..538c64a58 --- /dev/null +++ b/api/app/tests/auth/test_reminder_job_routes.py @@ -0,0 +1,38 @@ +import pytest +from app.tests.api_test_support import ( + assert_forbidden, + assert_json_response, + assert_unauthorized, + json_of, +) + +pytestmark = [pytest.mark.auth, pytest.mark.usefixtures("seeded_database")] + + +def test_bare_client_receives_401_for_reminder_job_routes(bare_client): + """Assert that reminder-job routes still reject unauthenticated callers.""" + response = bare_client.get("/appointment/reminders/email/") + + assert_unauthorized(response) + + +def test_internal_user_receives_403_for_reminder_job_routes(internal_ga_client): + """Assert that regular internal identities remain forbidden from reminder-job routes.""" + response = internal_ga_client.get("/appointment/reminders/email/") + + assert_forbidden(response) + + +def test_public_user_receives_403_for_reminder_job_routes(public_client): + """Assert that public identities remain forbidden from reminder-job routes.""" + response = public_client.get("/appointment/reminders/email/") + + assert_forbidden(response) + + +def test_reminder_job_identity_can_access_reminder_routes(reminder_job_client): + """Assert that the synthetic reminder-job identity can still reach reminder routes.""" + response = reminder_job_client.get("/appointment/reminders/email/") + + assert_json_response(response, 200) + assert "appointments" in json_of(response) diff --git a/api/app/tests/conftest.py b/api/app/tests/conftest.py index 4ccba9764..9cf7b605e 100644 --- a/api/app/tests/conftest.py +++ b/api/app/tests/conftest.py @@ -1,413 +1,53 @@ -import copy -import importlib -import os -import sys -import uuid -from contextlib import closing +from pathlib import Path import pytest -from app.tests.api_test_support import ApiClient +pytest_plugins = [ + "app.tests.fixtures.auth", + "app.tests.fixtures.db", + "app.tests.fixtures.smoke", +] -try: - import psycopg2 - from psycopg2 import sql -except ImportError: # pragma: no cover - handled by session skip - psycopg2 = None - sql = None - - -TEST_IDENTITIES = { - "internal_ga": { - "username": "cfms-postman-operator", - "identity_provider": "idir", - "realm_access": {"roles": ["internal_user"]}, - "email": "ga@example.com", - "display_name": "GA User", - "family_name": "GA", - }, - "internal_nonqtxn": { - "username": "cfms-postman-non-operator", - "identity_provider": "idir", - "realm_access": {"roles": ["internal_user"]}, - "email": "csr@example.com", - "display_name": "CSR User", - "family_name": "CSR", - }, - "public_user": { - "username": "theq-public-user", - "identity_provider": "bceid", - "realm_access": {"roles": ["online_appointment_user"]}, - "email": "public@example.com", - "display_name": "Public User", - "family_name": "Public", - }, - "public_user_alt": { - "username": "theq-public-user-alt", - "identity_provider": "bceid", - "realm_access": {"roles": ["online_appointment_user"]}, - "email": "public-alt@example.com", - "display_name": "Public Alt User", - "family_name": "PublicAlt", - }, +CONTRACT_SMOKE_FILES = { + "test_contract_helpers.py", + "test_contract_strictness.py", +} +DB_BACKED_TOP_LEVEL_FILES = { + "test_flask3_admin.py", + "test_schema_compatibility.py", + "test_sqlalchemy.py", } -def _db_settings(): - return { - "engine": os.getenv("TEST_DATABASE_ENGINE", "postgresql+psycopg2"), - "host": os.getenv("TEST_DATABASE_HOST", os.getenv("DATABASE_HOST", "127.0.0.1")), - "port": os.getenv("TEST_DATABASE_PORT", os.getenv("DATABASE_PORT", "5432")), - "user": os.getenv("TEST_DATABASE_USERNAME", os.getenv("DATABASE_USERNAME", "postgres")), - "password": os.getenv("TEST_DATABASE_PASSWORD", os.getenv("DATABASE_PASSWORD", "root")), - "admin_db": os.getenv("TEST_DATABASE_ADMIN_DB", "postgres"), - } - - -def _connect(database_name): - settings = _db_settings() - return psycopg2.connect( - dbname=database_name, - user=settings["user"], - password=settings["password"], - host=settings["host"], - port=settings["port"], - ) - - -def _patch_jwt_manager(): - from flask_jwt_oidc import JwtManager - - if getattr(JwtManager, "_theq_pytest_patched", False): - return - - def passthrough(self, *decorator_args, **decorator_kwargs): - del self, decorator_kwargs - if decorator_args and callable(decorator_args[0]) and len(decorator_args) == 1: - return decorator_args[0] - - def decorator(func): - return func - - return decorator - - def init_app(self, app): - app.extensions["theq_test_jwt"] = self - - JwtManager.has_one_of_roles = passthrough - JwtManager.requires_auth = passthrough - JwtManager.requires_auth_cookie = passthrough - JwtManager.init_app = init_app - JwtManager._theq_pytest_patched = True - - -def _install_identity_loader(app): - if app.config.get("THEQ_TEST_IDENTITY_LOADER"): - return - - from flask import g, request - - app.config["TEST_IDENTITIES"] = TEST_IDENTITIES - app.config["DISABLE_AUTO_REFRESH"] = True - - @app.before_request - def _load_theq_test_identity(): - identity_name = request.headers.get("X-TheQ-Test-Identity") - identity = app.config["TEST_IDENTITIES"].get(identity_name) - if identity: - g.jwt_oidc_token_info = copy.deepcopy(identity) - - app.config["THEQ_TEST_IDENTITY_LOADER"] = True - - -def _stub_integrations(): - import qsystem - from app.utilities.snowplow import SnowPlow - - qsystem.socketio.emit = lambda *args, **kwargs: None - - for method_name in ( - "add_citizen", - "choose_service", - "snowplow_event", - "snowplow_appointment", - ): - setattr(SnowPlow, method_name, staticmethod(lambda *args, **kwargs: None)) - - email_stub = lambda *args, **kwargs: None - email_contents_stub = lambda *args, **kwargs: ("test@example.com", "Subject", "Body") - sms_stub = lambda *args, **kwargs: True - - patch_targets = { - "app.resources.theq.citizen.citizen_add_to_queue": { - "send_email": email_stub, - "get_walkin_spot_confirmation_email_contents": email_contents_stub, - "send_walkin_spot_confirmation_sms": sms_stub, - }, - "app.resources.theq.citizen.citizen_detail": { - "send_email": email_stub, - "get_walkin_reminder_email_contents": email_contents_stub, - "send_walkin_reminder_sms": sms_stub, - }, - "app.resources.theq.user.user": { - "send_sms": sms_stub, - }, - "app.resources.bookings.appointment.appointment_post": { - "send_email": email_stub, - "get_confirmation_email_contents": email_contents_stub, - "get_blackout_email_contents": email_contents_stub, - "send_sms": sms_stub, - }, - "app.resources.bookings.appointment.appointment_put": { - "send_email": email_stub, - "get_confirmation_email_contents": email_contents_stub, - "send_sms": sms_stub, - }, - "app.resources.bookings.appointment.appointment_delete": { - "send_email": email_stub, - "get_cancel_email_contents": email_contents_stub, - }, - } - - for module_name, attributes in patch_targets.items(): - module = importlib.import_module(module_name) - for attribute_name, value in attributes.items(): - setattr(module, attribute_name, value) - - -@pytest.fixture(scope="session") -def postgres_database(): - if psycopg2 is None: - pytest.skip("psycopg2 is required for the SQLAlchemy smoke suite") - - database_name = f"qsystem_sqlalchemy_smoke_{uuid.uuid4().hex[:12]}" - settings = _db_settings() - - try: - with closing(_connect(settings["admin_db"])) as connection: - connection.autocommit = True - with connection.cursor() as cursor: - cursor.execute( - sql.SQL("CREATE DATABASE {}").format(sql.Identifier(database_name)) - ) - except Exception as exc: # pragma: no cover - depends on local services - pytest.skip(f"unable to create disposable Postgres database: {exc}") - - original_env = { - key: os.environ.get(key) - for key in ( - "FLASK_CONFIGURATION", - "DATABASE_ENGINE", - "DATABASE_HOST", - "DATABASE_PORT", - "DATABASE_USERNAME", - "DATABASE_PASSWORD", - "DATABASE_NAME", - "JWT_OIDC_WELL_KNOWN_CONFIG", - "JWT_OIDC_JWKS_URI", - "JWT_OIDC_ISSUER", - "JWT_OIDC_AUDIENCE", - ) - } - - os.environ.update( - { - "FLASK_CONFIGURATION": "localhost", - "DATABASE_ENGINE": settings["engine"], - "DATABASE_HOST": settings["host"], - "DATABASE_PORT": settings["port"], - "DATABASE_USERNAME": settings["user"], - "DATABASE_PASSWORD": settings["password"], - "DATABASE_NAME": database_name, - "JWT_OIDC_WELL_KNOWN_CONFIG": "", - "JWT_OIDC_JWKS_URI": "https://example.com/jwks.json", - "JWT_OIDC_ISSUER": "https://example.com/", - "JWT_OIDC_AUDIENCE": "queue-api-tests", - } +def pytest_addoption(parser): + parser.addoption( + "--require-integration-db", + action="store_true", + default=False, + help="Fail fast when the integration database is unavailable.", ) - try: - yield { - "database_name": database_name, - "database_uri": ( - f"{settings['engine']}://{settings['user']}:{settings['password']}" - f"@{settings['host']}:{settings['port']}/{database_name}" - ), - } - finally: - for module_name in ("manage", "qsystem"): - module = sys.modules.get(module_name) - if module is not None and hasattr(module, "db"): - try: - module.db.session.remove() - if hasattr(module, "application"): - with module.application.app_context(): - module.db.engine.dispose() - except Exception: - pass - - for module_name in list(sys.modules): - if module_name == "app" or module_name.startswith("app.") or module_name in ("manage", "qsystem"): - sys.modules.pop(module_name, None) - - try: - with closing(_connect(settings["admin_db"])) as connection: - connection.autocommit = True - with connection.cursor() as cursor: - cursor.execute( - sql.SQL( - "SELECT pg_terminate_backend(pid) " - "FROM pg_stat_activity WHERE datname = %s AND pid <> pg_backend_pid()" - ), - (database_name,), - ) - cursor.execute( - sql.SQL("DROP DATABASE IF EXISTS {}").format(sql.Identifier(database_name)) - ) - finally: - for key, value in original_env.items(): - if value is None: - os.environ.pop(key, None) - else: - os.environ[key] = value - - -@pytest.fixture(scope="session") -def app_module(postgres_database): - del postgres_database - _patch_jwt_manager() - module = importlib.import_module("manage") - _install_identity_loader(module.application) - _stub_integrations() - return module - - -@pytest.fixture(scope="session") -def app(app_module): - return app_module.application - - -@pytest.fixture(scope="session") -def db(app_module): - return app_module.db - - -@pytest.fixture(scope="session") -def cli_runner(app): - return app.test_cli_runner() - - -@pytest.fixture(scope="session") -def client(app): - return app.test_client() - - -@pytest.fixture(scope="session") -def migrated_database(cli_runner): - result = cli_runner.invoke(args=["db", "upgrade"]) - assert result.exit_code == 0, result.output - return result - - -@pytest.fixture() -def seeded_database(cli_runner, migrated_database, app): - del migrated_database - - with app.app_context(): - from qsystem import db - - db.session.remove() - - result = cli_runner.invoke(args=["bootstrap"]) - assert result.exit_code == 0, result.output - - with app.app_context(): - from qsystem import cache, db - - db.session.remove() - cache.clear() - - return result - - -@pytest.fixture() -def seeded_data(seeded_database, app): - del seeded_database - - with app.app_context(): - from app.models.bookings import ExamType, Invigilator, Room - from app.models.theq import CSR, Channel, Counter, Office, Service - - office_test = Office.query.filter_by(office_name="Test Office").first() - office_limited = Office.query.filter_by(office_name="100 Mile House").first() - quick_trans = Counter.query.filter_by(counter_name="Quick Trans").first() - counter = Counter.query.filter_by(counter_name="Counter").first() - phone_channel = Channel.query.filter_by(channel_name="Phone").first() - email_channel = Channel.query.filter_by(channel_name="Email/Fax/Mail").first() - msp_service = Service.query.filter_by(service_name="Payment - MSP").first() - ptax_service = Service.query.filter_by(service_name="Other - PTAX").first() - limited_office_service = Service.query.filter_by(service_name="Deferment Application").first() - room = Room.query.filter_by(room_name="Boardroom 1").first() - invigilators = Invigilator.query.filter_by(office_id=office_test.office_id).order_by(Invigilator.invigilator_id).all() - exam_type = ExamType.query.order_by(ExamType.exam_type_id).first() - ga = CSR.query.filter_by(username="cfms-postman-operator").first() - non_qtxn = CSR.query.filter_by(username="cfms-postman-non-operator").first() - - return { - "office_ids": { - "test_office": office_test.office_id, - "limited_office": office_limited.office_id, - }, - "office_timezones": { - "test_office": office_test.timezone.timezone_name, - "limited_office": office_limited.timezone.timezone_name, - }, - "counter_ids": { - "quick_trans": quick_trans.counter_id, - "counter": counter.counter_id, - }, - "channel_ids": { - "phone": phone_channel.channel_id, - "email": email_channel.channel_id, - }, - "service_ids": { - "msp": msp_service.service_id, - "ptax": ptax_service.service_id, - "limited_office_service": limited_office_service.service_id, - }, - "csr_ids": { - "ga": ga.csr_id, - "non_qtxn": non_qtxn.csr_id, - }, - "room_id": room.room_id, - "invigilator_ids": [invigilator.invigilator_id for invigilator in invigilators], - "exam_type_id": exam_type.exam_type_id, - } - - -@pytest.fixture() -def api_client_factory(app): - def factory(identity_name): - return ApiClient(app.test_client(), identity_name) - - return factory - - -@pytest.fixture() -def internal_ga_client(api_client_factory): - return api_client_factory("internal_ga") - -@pytest.fixture() -def internal_nonqtxn_client(api_client_factory): - return api_client_factory("internal_nonqtxn") +def _is_integration_path(path: Path) -> bool: + path_str = path.as_posix() + if "/app/tests/auth/" in path_str: + return True + if "/app/tests/flows/" in path_str: + return True + if "/app/tests/validation/" in path_str: + return True + if "/app/tests/contracts/" in path_str and path.name not in CONTRACT_SMOKE_FILES: + return True + if path.name in DB_BACKED_TOP_LEVEL_FILES: + return True + return False -@pytest.fixture() -def public_client(api_client_factory): - return api_client_factory("public_user") +def pytest_collection_modifyitems(config, items): + del config -@pytest.fixture() -def public_client_alt(api_client_factory): - return api_client_factory("public_user_alt") + for item in items: + path = Path(str(item.fspath)) + if _is_integration_path(path) and not item.get_closest_marker("integration"): + item.add_marker(pytest.mark.integration) diff --git a/api/app/tests/contracts/__init__.py b/api/app/tests/contracts/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/api/app/tests/contracts/__init__.py @@ -0,0 +1 @@ + diff --git a/api/app/tests/contracts/conftest.py b/api/app/tests/contracts/conftest.py new file mode 100644 index 000000000..22112db3c --- /dev/null +++ b/api/app/tests/contracts/conftest.py @@ -0,0 +1,17 @@ +import jsonschema +import pytest + + +def validate_schema(response_json, schema): + __tracebackhide__ = True + try: + jsonschema.validate(instance=response_json, schema=schema) + except jsonschema.ValidationError as error: + path = ".".join(str(part) for part in error.absolute_path) + location = path or "" + if error.validator == "required" and error.message.startswith("'"): + missing_property = error.message.split("'", 2)[1] + location = f"{path}.{missing_property}" if path else missing_property + pytest.fail(f"Schema validation failed at {location}: {error.message}") + except jsonschema.SchemaError as error: + pytest.fail(f"Invalid schema: {error.message}") diff --git a/api/app/tests/contracts/schemas.py b/api/app/tests/contracts/schemas.py new file mode 100644 index 000000000..61403f3a5 --- /dev/null +++ b/api/app/tests/contracts/schemas.py @@ -0,0 +1,817 @@ +UTC_DATETIME_SCHEMA = { + "type": "string", + "pattern": r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$", +} +ISO_DATETIME_SCHEMA = { + "type": "string", + "pattern": r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:(?:Z|[+-]\d{2}:\d{2}))?$", +} +DATE_KEY_SCHEMA = {"type": "string", "pattern": r"^\d{2}/\d{2}/\d{4}$"} + + +def nullable(schema): + return {"anyOf": [schema, {"type": "null"}]} + + +def object_schema(*, required, properties, additional_properties=False): + return { + "type": "object", + "required": list(required), + "properties": properties, + "additionalProperties": additional_properties, + } + + +TIMEZONE_SCHEMA = object_schema( + required=["timezone_id", "timezone_name"], + properties={ + "timezone_id": {"type": "integer"}, + "timezone_name": {"type": "string"}, + }, +) + +COUNTER_SCHEMA = object_schema( + required=["counter_id", "counter_name"], + properties={ + "counter_id": {"type": "integer"}, + "counter_name": {"type": "string"}, + }, +) + +SMARTBOARD_SCHEMA = object_schema( + required=["sb_id", "sb_type"], + properties={ + "sb_id": {"type": "integer"}, + "sb_type": {"type": "string"}, + }, +) + +CHANNEL_SCHEMA = object_schema( + required=["channel_id", "channel_name"], + properties={ + "channel_id": {"type": "integer"}, + "channel_name": {"type": "string"}, + }, +) + +SERVICE_PARENT_SCHEMA = object_schema( + required=["service_name"], + properties={"service_name": {"type": "string"}}, +) + +SERVICE_SCHEMA = object_schema( + required=[ + "service_id", + "service_name", + "parent_id", + "display_dashboard_ind", + "actual_service_ind", + ], + properties={ + "service_id": {"type": "integer"}, + "service_code": nullable({"type": "string"}), + "service_name": {"type": "string"}, + "service_desc": nullable({"type": "string"}), + "parent": nullable(SERVICE_PARENT_SCHEMA), + "parent_id": nullable({"type": "integer"}), + "deleted": nullable(ISO_DATETIME_SCHEMA), + "prefix": nullable({"type": "string"}), + "display_dashboard_ind": {"type": "integer"}, + "actual_service_ind": {"type": "integer"}, + "external_service_name": nullable({"type": "string"}), + "online_link": nullable({"type": "string"}), + "online_availability": nullable({"type": "string"}), + "timeslot_duration": nullable({"type": "integer"}), + "email_paragraph": nullable({"type": "string"}), + "css_colour": nullable({"type": "string"}), + "is_dlkt": nullable({"type": "boolean"}), + }, +) + +CATEGORY_SCHEMA = SERVICE_SCHEMA + +SERVICE_REQUEST_SERVICE_SCHEMA = object_schema( + required=["service_name"], + properties={ + "service_name": {"type": "string"}, + "parent": nullable(SERVICE_PARENT_SCHEMA), + "parent_id": nullable({"type": "integer"}), + "external_service_name": nullable({"type": "string"}), + "online_link": nullable({"type": "string"}), + "online_availability": nullable({"type": "string"}), + "timeslot_duration": nullable({"type": "integer"}), + "email_paragraph": nullable({"type": "string"}), + "css_colour": nullable({"type": "string"}), + "is_dlkt": nullable({"type": "boolean"}), + }, +) + +ROLE_SCHEMA = object_schema( + required=["role_id", "role_code"], + properties={ + "role_id": {"type": "integer"}, + "role_code": {"type": "string"}, + "role_desc": nullable({"type": "string"}), + }, +) + +CSR_STATE_SCHEMA = object_schema( + required=["csr_state_id", "csr_state_name"], + properties={ + "csr_state_id": {"type": "integer"}, + "csr_state_name": {"type": "string"}, + "csr_state_desc": nullable({"type": "string"}), + }, +) + +OFFICE_SUMMARY_SCHEMA = object_schema( + required=["office_id", "office_name", "office_number", "timezone"], + properties={ + "office_id": {"type": "integer"}, + "office_name": {"type": "string"}, + "office_number": {"type": "integer"}, + "appointments_enabled_ind": nullable({"type": "integer"}), + "exams_enabled_ind": nullable({"type": "integer"}), + "timezone": TIMEZONE_SCHEMA, + }, +) + +OFFICE_APPOINTMENT_SCHEMA = object_schema( + required=["office_id", "office_name", "office_number", "timezone"], + properties={ + "office_id": {"type": "integer"}, + "office_name": {"type": "string"}, + "office_number": {"type": "integer"}, + "sb_id": nullable({"type": "integer"}), + "deleted": nullable(ISO_DATETIME_SCHEMA), + "exams_enabled_ind": nullable({"type": "integer"}), + "appointments_enabled_ind": nullable({"type": "integer"}), + "max_person_appointment_per_day": nullable({"type": "integer"}), + "telephone": nullable({"type": "string"}), + "appointments_days_limit": nullable({"type": "integer"}), + "appointment_duration": nullable({"type": "integer"}), + "timezone": TIMEZONE_SCHEMA, + "latitude": nullable({"type": "number"}), + "longitude": nullable({"type": "number"}), + "office_appointment_message": nullable({"type": "string"}), + "civic_address": nullable({"type": "string"}), + "online_status": nullable({"type": "string"}), + "optout_status": nullable({"type": "integer"}), + "external_map_link": nullable({"type": "string"}), + "check_in_notification": nullable({"type": "integer"}), + "check_in_reminder_msg": nullable({"type": "string"}), + "automatic_reminder_at": nullable({"type": "integer"}), + "currently_waiting": nullable({"type": "integer"}), + "digital_signage_message": nullable({"type": "integer"}), + "digital_signage_message_1": nullable({"type": "string"}), + "digital_signage_message_2": nullable({"type": "string"}), + "digital_signage_message_3": nullable({"type": "string"}), + "show_currently_waiting_bottom": nullable({"type": "integer"}), + }, +) + +ROOM_SCHEMA = object_schema( + required=["room_id", "room_name", "capacity", "color", "office"], + properties={ + "room_id": {"type": "integer"}, + "room_name": {"type": "string"}, + "capacity": {"type": "integer"}, + "color": nullable({"type": "string"}), + "deleted": nullable({"type": "string"}), + "office": OFFICE_SUMMARY_SCHEMA, + }, +) + +BOOKING_ROOM_SCHEMA = object_schema( + required=["room_id", "room_name", "capacity", "color"], + properties={ + "room_id": {"type": "integer"}, + "room_name": {"type": "string"}, + "capacity": {"type": "integer"}, + "color": nullable({"type": "string"}), + "deleted": nullable({"type": "string"}), + }, +) + +INVIGILATOR_SCHEMA = object_schema( + required=[ + "invigilator_id", + "invigilator_name", + "shadow_count", + "shadow_flag", + "office_id", + ], + properties={ + "invigilator_id": {"type": "integer"}, + "invigilator_name": {"type": "string"}, + "shadow_count": {"type": "integer"}, + "shadow_flag": {"type": "string"}, + "office_id": {"type": "integer"}, + "contact_phone": nullable({"type": "string"}), + "contact_email": nullable({"type": "string"}), + "contract_number": nullable({"type": "string"}), + "contract_expiry_date": nullable({"type": "string"}), + "invigilator_notes": nullable({"type": "string"}), + "deleted": nullable({"type": "string"}), + "office": OFFICE_SUMMARY_SCHEMA, + }, +) + +EXAM_TYPE_SCHEMA = object_schema( + required=[ + "exam_type_id", + "exam_type_name", + "number_of_hours", + "number_of_minutes", + "group_exam_ind", + "pesticide_exam_ind", + ], + properties={ + "exam_type_id": {"type": "integer"}, + "exam_type_name": {"type": "string"}, + "exam_color": nullable({"type": "string"}), + "number_of_hours": {"type": "integer"}, + "number_of_minutes": {"type": "integer"}, + "method_type": nullable({"type": "string"}), + "ita_ind": nullable({"type": "integer"}), + "group_exam_ind": {"type": "integer"}, + "pesticide_exam_ind": {"type": "integer"}, + "deleted": nullable({"type": "string"}), + }, +) + +PERIOD_STATE_SCHEMA = object_schema( + required=["ps_name"], + properties={ + "ps_id": nullable({"type": "integer"}), + "ps_name": {"type": "string"}, + "ps_desc": nullable({"type": "string"}), + "ps_number": nullable({"type": "integer"}), + }, +) + +SR_STATE_SCHEMA = object_schema( + required=["sr_code"], + properties={ + "sr_state_id": nullable({"type": "integer"}), + "sr_code": {"type": "string"}, + "sr_state_desc": nullable({"type": "string"}), + }, +) + +CITIZEN_STATE_SCHEMA = object_schema( + required=["cs_state_name"], + properties={ + "cs_id": nullable({"type": "integer"}), + "cs_state_name": {"type": "string"}, + "cs_state_desc": nullable({"type": "string"}), + }, +) + +PERIOD_CSR_SCHEMA = object_schema( + required=["username", "counter_id", "counter"], + properties={ + "username": {"type": "string"}, + "counter_id": {"type": "integer"}, + "counter": {"type": "integer"}, + "qt_xn_csr_ind": nullable({"type": "integer"}), + }, +) + +PERIOD_SCHEMA = object_schema( + required=["period_id", "time_start", "ps", "csr"], + properties={ + "period_id": {"type": "integer"}, + "sr_id": nullable({"type": "integer"}), + "csr_id": nullable({"type": "integer"}), + "reception_csr_ind": nullable({"type": "integer"}), + "ps_id": nullable({"type": "integer"}), + "time_start": nullable(ISO_DATETIME_SCHEMA), + "time_end": nullable(ISO_DATETIME_SCHEMA), + "ps": PERIOD_STATE_SCHEMA, + "csr": PERIOD_CSR_SCHEMA, + }, +) + +SERVICE_NAME_ONLY_SCHEMA = object_schema( + required=["service_name"], + properties={"service_name": {"type": "string"}}, +) + +CHANNEL_NAME_ONLY_SCHEMA = object_schema( + required=["channel_name"], + properties={"channel_name": {"type": "string"}}, +) + +SERVICE_REQUEST_SCHEMA = object_schema( + required=[ + "sr_id", + "citizen_id", + "channel_id", + "service_id", + "quantity", + "sr_number", + "periods", + "sr_state", + "service", + "channel", + ], + properties={ + "sr_id": {"type": "integer"}, + "citizen_id": {"type": "integer"}, + "channel_id": {"type": "integer"}, + "service_id": {"type": "integer"}, + "quantity": {"type": "integer"}, + "sr_number": {"type": "integer"}, + "periods": {"type": "array", "items": PERIOD_SCHEMA}, + "sr_state": SR_STATE_SCHEMA, + "service": SERVICE_REQUEST_SERVICE_SCHEMA, + "channel": CHANNEL_NAME_ONLY_SCHEMA, + }, +) + +CITIZEN_SCHEMA = object_schema( + required=["citizen_id", "citizen_name", "office_id", "service_reqs", "cs"], + properties={ + "citizen_id": {"type": "integer"}, + "citizen_name": nullable({"type": "string"}), + "office_id": {"type": "integer"}, + "ticket_number": nullable({"type": "string"}), + "citizen_comments": nullable({"type": "string"}), + "qt_xn_citizen_ind": nullable({"type": "integer"}), + "counter_id": nullable({"type": "integer"}), + "start_time": nullable(UTC_DATETIME_SCHEMA), + "accurate_time_ind": nullable({"type": "integer"}), + "service_reqs": {"type": "array", "items": SERVICE_REQUEST_SCHEMA}, + "cs": CITIZEN_STATE_SCHEMA, + "priority": nullable({"type": "integer"}), + "user_id": nullable({"type": "integer"}), + "notification_sent_time": nullable(ISO_DATETIME_SCHEMA), + "notification_phone": nullable({"type": "string"}), + "notification_email": nullable({"type": "string"}), + "reminder_flag": nullable({"type": "integer"}), + "walkin_unique_id": nullable({"type": "string"}), + "automatic_reminder_flag": nullable({"type": "integer"}), + "created_at": nullable(ISO_DATETIME_SCHEMA), + }, +) + +SERVICE_REQUEST_CITIZEN_SCHEMA = object_schema( + required=["citizen_id", "citizen_name", "office_id", "cs"], + properties={ + "citizen_id": {"type": "integer"}, + "citizen_name": nullable({"type": "string"}), + "office_id": {"type": "integer"}, + "ticket_number": nullable({"type": "string"}), + "citizen_comments": nullable({"type": "string"}), + "qt_xn_citizen_ind": nullable({"type": "integer"}), + "counter_id": nullable({"type": "integer"}), + "start_time": nullable(UTC_DATETIME_SCHEMA), + "accurate_time_ind": nullable({"type": "integer"}), + "cs": CITIZEN_STATE_SCHEMA, + "priority": nullable({"type": "integer"}), + "user_id": nullable({"type": "integer"}), + "notification_sent_time": nullable(ISO_DATETIME_SCHEMA), + "notification_phone": nullable({"type": "string"}), + "notification_email": nullable({"type": "string"}), + "reminder_flag": nullable({"type": "integer"}), + "walkin_unique_id": nullable({"type": "string"}), + "automatic_reminder_flag": nullable({"type": "integer"}), + "created_at": nullable(ISO_DATETIME_SCHEMA), + }, +) + +SERVICE_REQUEST_SCHEMA["properties"]["citizen"] = SERVICE_REQUEST_CITIZEN_SCHEMA + +OFFICE_SCHEMA = object_schema( + required=[ + "office_id", + "office_name", + "office_number", + "timezone", + "counters", + "timeslots", + ], + properties={ + "office_id": {"type": "integer"}, + "office_name": {"type": "string"}, + "office_number": {"type": "integer"}, + "sb_id": nullable({"type": "integer"}), + "deleted": nullable(ISO_DATETIME_SCHEMA), + "exams_enabled_ind": nullable({"type": "integer"}), + "appointments_enabled_ind": nullable({"type": "integer"}), + "max_person_appointment_per_day": nullable({"type": "integer"}), + "telephone": nullable({"type": "string"}), + "appointments_days_limit": nullable({"type": "integer"}), + "appointment_duration": nullable({"type": "integer"}), + "sb": nullable(SMARTBOARD_SCHEMA), + "timezone": TIMEZONE_SCHEMA, + "counters": {"type": "array", "items": COUNTER_SCHEMA}, + "quick_list": {"type": "array", "items": SERVICE_SCHEMA}, + "back_office_list": {"type": "array", "items": SERVICE_SCHEMA}, + "timeslots": {"type": "array", "items": {"type": "object"}}, + "latitude": nullable({"type": "number"}), + "longitude": nullable({"type": "number"}), + "office_appointment_message": nullable({"type": "string"}), + "civic_address": nullable({"type": "string"}), + "online_status": nullable({"type": "string"}), + "optout_status": nullable({"type": "integer"}), + "external_map_link": nullable({"type": "string"}), + "check_in_notification": nullable({"type": "integer"}), + "check_in_reminder_msg": nullable({"type": "string"}), + "automatic_reminder_at": nullable({"type": "integer"}), + "currently_waiting": nullable({"type": "integer"}), + "digital_signage_message": nullable({"type": "integer"}), + "digital_signage_message_1": nullable({"type": "string"}), + "digital_signage_message_2": nullable({"type": "string"}), + "digital_signage_message_3": nullable({"type": "string"}), + "show_currently_waiting_bottom": nullable({"type": "integer"}), + }, +) + +ROOM_SCHEMA["properties"]["office"] = OFFICE_SCHEMA +INVIGILATOR_SCHEMA["properties"]["office"] = OFFICE_SCHEMA + +APPOINTMENT_SCHEMA = object_schema( + required=[ + "appointment_id", + "office_id", + "start_time", + "end_time", + "citizen_name", + ], + properties={ + "appointment_id": {"type": "integer"}, + "office_id": {"type": "integer"}, + "service_id": nullable({"type": "integer"}), + "citizen_id": nullable({"type": "integer"}), + "start_time": nullable(ISO_DATETIME_SCHEMA), + "end_time": nullable(ISO_DATETIME_SCHEMA), + "checked_in_time": nullable(ISO_DATETIME_SCHEMA), + "comments": nullable({"type": "string"}), + "citizen_name": {"type": "string"}, + "contact_information": nullable({"type": "string"}), + "blackout_flag": nullable({"type": "string"}), + "recurring_uuid": nullable({"type": "string"}), + "online_flag": nullable({"type": "boolean"}), + "is_draft": nullable({"type": "boolean"}), + "stat_flag": nullable({"type": "boolean"}), + "office": nullable(OFFICE_APPOINTMENT_SCHEMA), + "service": nullable(SERVICE_SCHEMA), + }, +) + +BOOKING_SCHEMA = object_schema( + required=["booking_id", "office_id", "start_time", "end_time", "invigilators"], + properties={ + "booking_id": {"type": "integer"}, + "booking_name": nullable({"type": "string"}), + "end_time": nullable(ISO_DATETIME_SCHEMA), + "fees": nullable({"type": "string"}), + "room_id": nullable({"type": "integer"}), + "start_time": nullable(ISO_DATETIME_SCHEMA), + "shadow_invigilator_id": nullable({"type": "integer"}), + "office_id": {"type": "integer"}, + "sbc_staff_invigilated": nullable({"type": "integer"}), + "booking_contact_information": nullable({"type": "string"}), + "blackout_flag": nullable({"type": "string"}), + "blackout_notes": nullable({"type": "string"}), + "recurring_uuid": nullable({"type": "string"}), + "stat_flag": nullable({"type": "boolean"}), + "room": nullable(BOOKING_ROOM_SCHEMA), + "office": nullable(OFFICE_SUMMARY_SCHEMA), + "invigilators": {"type": "array", "items": {"type": "integer"}}, + }, +) + +EXAM_SCHEMA = object_schema( + required=[ + "exam_id", + "office_id", + "exam_type_id", + "exam_name", + "exam_written_ind", + ], + properties={ + "booking_id": nullable({"type": "integer"}), + "deleted_date": nullable({"type": "string"}), + "event_id": nullable({"type": "string"}), + "exam_destroyed_date": nullable({"type": "string"}), + "exam_id": {"type": "integer"}, + "exam_method": nullable({"type": "string"}), + "exam_name": {"type": "string"}, + "exam_received": nullable({"type": "integer"}), + "exam_received_date": nullable(UTC_DATETIME_SCHEMA), + "exam_type_id": {"type": "integer"}, + "examinee_name": nullable({"type": "string"}), + "examinee_phone": nullable({"type": "string"}), + "examinee_email": nullable({"type": "string"}), + "expiry_date": nullable(ISO_DATETIME_SCHEMA), + "notes": nullable({"type": "string"}), + "number_of_students": nullable({"type": "integer"}), + "office_id": {"type": "integer"}, + "invigilator_id": nullable({"type": "integer"}), + "session_number": nullable({"type": "integer"}), + "exam_returned_ind": nullable({"type": "integer"}), + "exam_returned_date": nullable({"type": "string"}), + "exam_returned_tracking_number": nullable({"type": "string"}), + "exam_written_ind": {"type": "integer"}, + "upload_received_ind": nullable({"type": "integer"}), + "offsite_location": nullable({"type": "string"}), + "sbc_managed_ind": nullable({"type": "integer"}), + "receipt": nullable({"type": "string"}), + "receipt_number": nullable({"type": "string"}), + "fees": nullable({"type": "string"}), + "payee_ind": nullable({"type": "integer"}), + "receipt_sent_ind": nullable({"type": "integer"}), + "payee_name": nullable({"type": "string"}), + "payee_email": nullable({"type": "string"}), + "payee_phone": nullable({"type": "string"}), + "bcmp_job_id": nullable({"type": "string"}), + "is_pesticide": nullable({"type": "integer"}), + "candidates_list": nullable({"type": "object"}), + "booking": nullable(BOOKING_SCHEMA), + "exam_type": nullable(EXAM_TYPE_SCHEMA), + "invigilator": nullable(INVIGILATOR_SCHEMA), + "office": nullable(OFFICE_SUMMARY_SCHEMA), + }, +) + +PUBLIC_USER_SCHEMA = object_schema( + required=[ + "telephone", + "send_email_reminders", + "email", + "display_name", + "last_name", + "username", + "user_id", + "send_sms_reminders", + ], + properties={ + "telephone": nullable({"type": "string"}), + "send_email_reminders": nullable({"type": "boolean"}), + "email": nullable({"type": "string"}), + "display_name": nullable({"type": "string"}), + "last_name": nullable({"type": "string"}), + "username": {"type": "string"}, + "user_id": {"type": "integer"}, + "send_sms_reminders": nullable({"type": "boolean"}), + }, +) + +SLOT_SCHEMA = object_schema( + required=["start_time", "end_time", "no_of_slots"], + properties={ + "start_time": {"type": "string", "pattern": r"^\d{2}:\d{2}$"}, + "end_time": {"type": "string", "pattern": r"^\d{2}:\d{2}$"}, + "no_of_slots": {"type": "integer"}, + }, +) + +SLOTS_SCHEMA = { + "type": "object", + "patternProperties": { + DATE_KEY_SCHEMA["pattern"]: {"type": "array", "items": SLOT_SCHEMA} + }, + "additionalProperties": False, +} + +REMINDER_APPOINTMENT_SCHEMA = object_schema( + required=[ + "formatted_date", + "day", + "email", + "display_name", + "location", + "duration", + "telephone", + "service_email_paragraph", + "office_email_paragraph", + "service_name", + "civic_address", + "user_telephone", + ], + properties={ + "formatted_date": {"type": "string"}, + "day": nullable({"type": "string"}), + "email": nullable({"type": "string"}), + "display_name": {"type": "string"}, + "location": {"type": "string"}, + "duration": {"type": "integer"}, + "telephone": nullable({"type": "string"}), + "service_email_paragraph": nullable({"type": "string"}), + "office_email_paragraph": nullable({"type": "string"}), + "service_name": {"type": "string"}, + "civic_address": nullable({"type": "string"}), + "user_telephone": nullable({"type": "string"}), + }, +) + +ERRORS_SCHEMA = {"type": "object"} + +CITIZEN_RESPONSE_SCHEMA = object_schema( + required=["citizen", "errors"], + properties={"citizen": CITIZEN_SCHEMA, "errors": ERRORS_SCHEMA}, +) + +SERVICE_REQUEST_RESPONSE_SCHEMA = object_schema( + required=["service_request", "errors"], + properties={ + "service_request": SERVICE_REQUEST_SCHEMA, + "errors": ERRORS_SCHEMA, + }, +) + +SERVICE_REQUEST_LIST_RESPONSE_SCHEMA = object_schema( + required=["service_requests", "errors"], + properties={ + "service_requests": {"type": "array", "items": SERVICE_REQUEST_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +APPOINTMENT_RESPONSE_SCHEMA = object_schema( + required=["appointment", "errors"], + properties={"appointment": APPOINTMENT_SCHEMA, "errors": ERRORS_SCHEMA}, +) + +APPOINTMENT_LIST_RESPONSE_SCHEMA = object_schema( + required=["appointments", "errors"], + properties={ + "appointments": {"type": "array", "items": APPOINTMENT_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +BOOKING_RESPONSE_SCHEMA = object_schema( + required=["booking", "errors"], + properties={"booking": BOOKING_SCHEMA, "errors": ERRORS_SCHEMA}, +) + +BOOKING_LIST_RESPONSE_SCHEMA = object_schema( + required=["bookings", "errors"], + properties={ + "bookings": {"type": "array", "items": BOOKING_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +EXAM_RESPONSE_SCHEMA = object_schema( + required=["exam", "errors"], + properties={"exam": EXAM_SCHEMA, "errors": ERRORS_SCHEMA}, +) + +EXAM_LIST_RESPONSE_SCHEMA = object_schema( + required=["exams", "errors"], + properties={ + "exams": {"type": "array", "items": EXAM_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +CHANNEL_LIST_RESPONSE_SCHEMA = object_schema( + required=["channels", "errors"], + properties={ + "channels": {"type": "array", "items": CHANNEL_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +CATEGORY_LIST_RESPONSE_SCHEMA = object_schema( + required=["categories", "errors"], + properties={ + "categories": {"type": "array", "items": CATEGORY_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +SERVICE_LIST_RESPONSE_SCHEMA = object_schema( + required=["services", "errors"], + properties={ + "services": {"type": "array", "items": SERVICE_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +OFFICE_LIST_RESPONSE_SCHEMA = object_schema( + required=["offices", "errors"], + properties={ + "offices": {"type": "array", "items": OFFICE_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +ROOM_LIST_RESPONSE_SCHEMA = object_schema( + required=["rooms", "errors"], + properties={ + "rooms": {"type": "array", "items": ROOM_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +INVIGILATOR_LIST_RESPONSE_SCHEMA = object_schema( + required=["invigilators", "errors"], + properties={ + "invigilators": {"type": "array", "items": INVIGILATOR_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +EXAM_TYPE_LIST_RESPONSE_SCHEMA = object_schema( + required=["exam_types", "errors"], + properties={ + "exam_types": {"type": "array", "items": EXAM_TYPE_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +CSR_SCHEMA = object_schema( + required=[ + "csr_id", + "username", + "office_id", + "role_id", + "csr_state_id", + "counter_id", + "counter", + "role", + "office", + "finance_designate", + "ita2_designate", + "pesticide_designate", + "qt_xn_csr_ind", + ], + properties={ + "csr_id": {"type": "integer"}, + "username": {"type": "string"}, + "office_id": {"type": "integer"}, + "role_id": {"type": "integer"}, + "receptionist_ind": nullable({"type": "integer"}), + "deleted": nullable(ISO_DATETIME_SCHEMA), + "csr_state_id": {"type": "integer"}, + "counter_id": {"type": "integer"}, + "counter": {"type": "integer"}, + "csr_state": nullable(CSR_STATE_SCHEMA), + "office": OFFICE_SCHEMA, + "role": ROLE_SCHEMA, + "office_manager": nullable({"type": "integer"}), + "pesticide_designate": nullable({"type": "integer"}), + "qt_xn_csr_ind": nullable({"type": "integer"}), + "finance_designate": nullable({"type": "integer"}), + "ita2_designate": nullable({"type": "integer"}), + }, +) + +CSR_LIST_ITEM_SCHEMA = object_schema( + required=[field for field in CSR_SCHEMA["required"] if field != "office"], + properties={ + key: value for key, value in CSR_SCHEMA["properties"].items() if key != "office" + }, +) + +CSR_LIST_RESPONSE_SCHEMA = object_schema( + required=["csrs", "errors"], + properties={ + "csrs": {"type": "array", "items": CSR_LIST_ITEM_SCHEMA}, + "errors": ERRORS_SCHEMA, + }, +) + +CSR_ME_RESPONSE_SCHEMA = object_schema( + required=[ + "csr", + "attention_needed", + "active_citizens", + "back_office_display", + "recurring_feature_flag", + "errors", + ], + properties={ + "csr": CSR_SCHEMA, + "attention_needed": {"type": "boolean"}, + "active_citizens": {"type": "array", "items": CITIZEN_SCHEMA}, + "back_office_display": {}, + "recurring_feature_flag": {}, + "errors": ERRORS_SCHEMA, + }, +) + +SMARTBOARD_SIDE_MENU_RESPONSE_SCHEMA = object_schema( + required=["office"], + properties={"office": OFFICE_SCHEMA}, +) + +PUBLIC_USER_LIST_SCHEMA = {"type": "array", "items": PUBLIC_USER_SCHEMA} + +USER_APPOINTMENTS_RESPONSE_SCHEMA = object_schema( + required=["appointments"], + properties={"appointments": {"type": "array", "items": APPOINTMENT_SCHEMA}}, +) + +REMINDER_RESPONSE_SCHEMA = object_schema( + required=["appointments"], + properties={ + "appointments": {"type": "array", "items": REMINDER_APPOINTMENT_SCHEMA} + }, +) diff --git a/api/app/tests/contracts/test_appointment_contracts.py b/api/app/tests/contracts/test_appointment_contracts.py new file mode 100644 index 000000000..43f70f20b --- /dev/null +++ b/api/app/tests/contracts/test_appointment_contracts.py @@ -0,0 +1,149 @@ +import pytest +from app.tests.api_test_support import ( + assert_json_response, + first_day_with_slots, + future_utc_window, + json_of, + slot_window_to_iso, + unique_name, +) +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import ( + APPOINTMENT_LIST_RESPONSE_SCHEMA, + APPOINTMENT_RESPONSE_SCHEMA, + SLOTS_SCHEMA, + USER_APPOINTMENTS_RESPONSE_SCHEMA, +) + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def _create_internal_appointment(api_client, seeded_data, *, days_from_now): + start_time, end_time = future_utc_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal appointment", + "citizen_name": unique_name("internal-appt"), + "contact_information": "internal@example.com", + }, + ) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + +def test_appointment_create_response_matches_the_contract( + internal_ga_client, seeded_data +): + """Assert that appointment creation returns the stable appointment payload contract.""" + start_time, end_time = future_utc_window(2) + response = internal_ga_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal appointment", + "citizen_name": unique_name("contract-appt"), + "contact_information": "internal@example.com", + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + validate_schema(body, APPOINTMENT_RESPONSE_SCHEMA) + assert body["appointment"]["citizen_name"] + + +def test_appointment_detail_response_matches_the_contract( + internal_ga_client, seeded_data +): + """Assert that appointment detail preserves the contract used by appointment edit screens.""" + appointment = _create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.get(f"/appointments/{appointment['appointment_id']}/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, APPOINTMENT_RESPONSE_SCHEMA) + assert body["appointment"]["appointment_id"] == appointment["appointment_id"] + + +def test_appointment_list_response_matches_the_contract( + internal_ga_client, seeded_data +): + """Assert that appointment list items preserve the contract used by calendar views.""" + appointment = _create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.get("/appointments/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, APPOINTMENT_LIST_RESPONSE_SCHEMA) + assert any( + item["appointment_id"] == appointment["appointment_id"] + for item in body["appointments"] + ) + + +def test_slots_response_matches_the_date_keyed_contract(public_client, seeded_data): + """Assert that public slot availability preserves the date-keyed slot contract.""" + response = public_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, SLOTS_SCHEMA) + + day_key, slots = first_day_with_slots(body) + assert day_key + assert slots[0]["no_of_slots"] >= 1 + + +def test_public_user_appointments_response_matches_the_contract( + public_client, seeded_data +): + """Assert that public-user appointment listings reuse the stable appointment contract.""" + create_user_response = public_client.post("/users/") + assert_json_response(create_user_response, 200) + + slots_response = public_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + assert_json_response(slots_response, 200) + + day_key, slots = first_day_with_slots(json_of(slots_response)) + start_time, end_time = slot_window_to_iso( + day_key, + slots[0], + seeded_data["office_timezones"]["limited_office"], + ) + create_appointment_response = public_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["limited_office_service"], + "office_id": seeded_data["office_ids"]["limited_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Public appointment", + "citizen_name": "Codex Public User", + "contact_information": "public@example.com", + }, + ) + assert_json_response(create_appointment_response, 201) + + response = public_client.get("/users/appointments/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, USER_APPOINTMENTS_RESPONSE_SCHEMA) diff --git a/api/app/tests/contracts/test_booking_contracts.py b/api/app/tests/contracts/test_booking_contracts.py new file mode 100644 index 000000000..aa4e4d693 --- /dev/null +++ b/api/app/tests/contracts/test_booking_contracts.py @@ -0,0 +1,77 @@ +import pytest +from app.tests.api_test_support import ( + assert_json_response, + future_utc_window, + json_of, + unique_name, +) +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import BOOKING_LIST_RESPONSE_SCHEMA, BOOKING_RESPONSE_SCHEMA + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def _create_booking(api_client, seeded_data, *, days_from_now): + start_time, end_time = future_utc_window(days_from_now, duration_minutes=120) + response = api_client.post( + "/bookings/", + json={ + "booking_name": unique_name("booking"), + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": seeded_data["office_ids"]["test_office"], + "room_id": seeded_data["room_id"], + "start_time": start_time, + "end_time": end_time, + "invigilator_id": [seeded_data["invigilator_ids"][0]], + }, + ) + assert_json_response(response, 201) + return json_of(response)["booking"] + + +def test_booking_create_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that booking creation returns the stable booking payload contract.""" + start_time, end_time = future_utc_window(2, duration_minutes=120) + response = internal_ga_client.post( + "/bookings/", + json={ + "booking_name": unique_name("booking"), + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": seeded_data["office_ids"]["test_office"], + "room_id": seeded_data["room_id"], + "start_time": start_time, + "end_time": end_time, + "invigilator_id": [seeded_data["invigilator_ids"][0]], + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + validate_schema(body, BOOKING_RESPONSE_SCHEMA) + assert body["booking"]["invigilators"] == [seeded_data["invigilator_ids"][0]] + + +def test_booking_detail_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that booking detail preserves the contract used by booking edit screens.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + response = internal_ga_client.get(f"/bookings/{booking['booking_id']}/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, BOOKING_RESPONSE_SCHEMA) + assert body["booking"]["booking_id"] == booking["booking_id"] + + +def test_booking_list_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that booking list items preserve the contract used by booking dashboards.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + response = internal_ga_client.get("/bookings/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, BOOKING_LIST_RESPONSE_SCHEMA) + assert any(item["booking_id"] == booking["booking_id"] for item in body["bookings"]) diff --git a/api/app/tests/contracts/test_citizen_contracts.py b/api/app/tests/contracts/test_citizen_contracts.py new file mode 100644 index 000000000..bc2adcbe6 --- /dev/null +++ b/api/app/tests/contracts/test_citizen_contracts.py @@ -0,0 +1,79 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import ( + CITIZEN_RESPONSE_SCHEMA, + SERVICE_REQUEST_LIST_RESPONSE_SCHEMA, +) + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def _create_citizen(api_client, position, *, name): + response = api_client.post( + f"/citizens/{position}/add_citizen/", + json={"citizen_name": name}, + ) + assert_json_response(response, 201) + return json_of(response)["citizen"] + + +def _create_service_request(api_client, citizen_id, seeded_data): + response = api_client.post( + "/service_requests/", + json={ + "service_request": { + "citizen_id": citizen_id, + "service_id": seeded_data["service_ids"]["ptax"], + "channel_id": seeded_data["channel_ids"]["phone"], + "quantity": 2, + } + }, + ) + assert_json_response(response, 201) + return json_of(response)["service_request"] + + +def test_citizen_detail_matches_the_nested_contract(internal_ga_client, seeded_data): + """Assert that citizen detail preserves the nested queue-state contract used by the frontend.""" + citizen = _create_citizen(internal_ga_client, 0, name="Contract Citizen") + _create_service_request(internal_ga_client, citizen["citizen_id"], seeded_data) + + response = internal_ga_client.get(f"/citizens/{citizen['citizen_id']}/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CITIZEN_RESPONSE_SCHEMA) + + citizen_detail = body["citizen"] + service_request = citizen_detail["service_reqs"][0] + period = service_request["periods"][0] + + assert citizen_detail["start_time"].endswith("Z") + assert citizen_detail["cs"]["cs_state_name"] == "Active" + assert service_request["sr_state"]["sr_code"] == "Active" + assert period["ps"]["ps_name"] == "Ticket Creation" + assert isinstance(period["csr"]["counter_id"], int) + + +def test_citizen_service_requests_endpoint_matches_the_nested_contract( + internal_ga_client, seeded_data +): + """Assert that the citizen service-request list returns the expected nested contract.""" + citizen = _create_citizen(internal_ga_client, 0, name="Contract Requests Citizen") + service_request = _create_service_request( + internal_ga_client, citizen["citizen_id"], seeded_data + ) + + response = internal_ga_client.get( + f"/citizens/{citizen['citizen_id']}/service_requests/" + ) + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, SERVICE_REQUEST_LIST_RESPONSE_SCHEMA) + + request_body = body["service_requests"][0] + assert request_body["sr_id"] == service_request["sr_id"] + assert request_body["sr_state"]["sr_code"] == "Active" + assert request_body["periods"][0]["ps"]["ps_name"] == "Ticket Creation" diff --git a/api/app/tests/contracts/test_contract_helpers.py b/api/app/tests/contracts/test_contract_helpers.py new file mode 100644 index 000000000..d3d606c73 --- /dev/null +++ b/api/app/tests/contracts/test_contract_helpers.py @@ -0,0 +1,46 @@ +import pytest +from app.tests.api_test_support import assert_json_response +from app.tests.contracts.conftest import validate_schema + +pytestmark = [pytest.mark.contracts, pytest.mark.smoke] + + +def test_assert_json_response_accepts_application_json(minimal_app): + """Assert that JSON responses with the expected status pass the shared helper.""" + response = minimal_app.response_class( + response='{"message":"ok"}', + status=200, + mimetype="application/json", + ) + + assert_json_response(response, 200) + + +def test_assert_json_response_rejects_non_json_content_type(minimal_app): + """Assert that the shared JSON helper rejects non-JSON content types.""" + response = minimal_app.response_class(response="ok", status=200, mimetype="text/html") + + with pytest.raises(AssertionError): + assert_json_response(response, 200) + + +def test_validate_schema_reports_the_failing_path(): + """Assert that schema validation failures include the nested field path.""" + with pytest.raises(pytest.fail.Exception, match=r"items\.0\.name"): + validate_schema( + {"items": [{}]}, + { + "type": "object", + "required": ["items"], + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + } + }, + }, + ) diff --git a/api/app/tests/contracts/test_contract_strictness.py b/api/app/tests/contracts/test_contract_strictness.py new file mode 100644 index 000000000..e1f540b5c --- /dev/null +++ b/api/app/tests/contracts/test_contract_strictness.py @@ -0,0 +1,138 @@ +import pytest +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import ( + APPOINTMENT_RESPONSE_SCHEMA, + CITIZEN_RESPONSE_SCHEMA, + PUBLIC_USER_LIST_SCHEMA, +) + +pytestmark = [pytest.mark.contracts, pytest.mark.smoke] + + +def test_appointment_contract_rejects_unexpected_fields(): + """Assert that appointment contracts fail when additive top-level fields sneak into the payload.""" + with pytest.raises(pytest.fail.Exception, match=r"appointment"): + validate_schema( + { + "appointment": { + "appointment_id": 1, + "office_id": 2, + "service_id": 3, + "citizen_id": 4, + "start_time": "2026-03-25T18:00:00+00:00", + "end_time": "2026-03-25T18:30:00+00:00", + "checked_in_time": None, + "comments": "Comment", + "citizen_name": "Pat Citizen", + "contact_information": "pat@example.com", + "blackout_flag": "N", + "recurring_uuid": None, + "online_flag": False, + "is_draft": False, + "stat_flag": False, + "office": None, + "service": None, + "unexpected": "nope", + }, + "errors": {}, + }, + APPOINTMENT_RESPONSE_SCHEMA, + ) + + +def test_public_user_contract_rejects_unexpected_fields(): + """Assert that public-user contracts reject additive profile fields.""" + with pytest.raises(pytest.fail.Exception, match=r"unexpected"): + validate_schema( + [ + { + "telephone": "2505550100", + "send_email_reminders": True, + "email": "public@example.com", + "display_name": "Public User", + "last_name": "User", + "username": "public@bceid", + "user_id": 7, + "send_sms_reminders": False, + "unexpected": True, + } + ], + PUBLIC_USER_LIST_SCHEMA, + ) + + +def test_citizen_contract_rejects_unexpected_nested_service_request_fields(): + """Assert that nested citizen/service-request contracts reject additive fields.""" + with pytest.raises(pytest.fail.Exception, match=r"unexpected"): + validate_schema( + { + "citizen": { + "citizen_id": 1, + "citizen_name": "Citizen", + "office_id": 2, + "ticket_number": None, + "citizen_comments": None, + "qt_xn_citizen_ind": 0, + "counter_id": None, + "start_time": "2026-03-25T18:00:00Z", + "accurate_time_ind": None, + "service_reqs": [ + { + "sr_id": 9, + "citizen_id": 1, + "channel_id": 3, + "service_id": 4, + "quantity": 1, + "sr_number": 12, + "periods": [ + { + "period_id": 8, + "sr_id": 9, + "csr_id": 6, + "reception_csr_ind": 0, + "ps_id": 2, + "time_start": "2026-03-25T18:00:00+00:00", + "time_end": None, + "ps": { + "ps_id": 2, + "ps_name": "Ticket Creation", + "ps_desc": None, + "ps_number": 2, + }, + "csr": { + "username": "tester", + "counter_id": 2, + "counter": 2, + "qt_xn_csr_ind": 0, + }, + } + ], + "sr_state": { + "sr_state_id": 1, + "sr_code": "Active", + "sr_state_desc": None, + }, + "service": {"service_name": "Property Tax"}, + "channel": {"channel_name": "Phone"}, + "unexpected": "x", + } + ], + "cs": { + "cs_id": 1, + "cs_state_name": "Active", + "cs_state_desc": None, + }, + "priority": None, + "user_id": None, + "notification_sent_time": None, + "notification_phone": None, + "notification_email": None, + "reminder_flag": None, + "walkin_unique_id": None, + "automatic_reminder_flag": None, + "created_at": None, + }, + "errors": {}, + }, + CITIZEN_RESPONSE_SCHEMA, + ) diff --git a/api/app/tests/contracts/test_exam_contracts.py b/api/app/tests/contracts/test_exam_contracts.py new file mode 100644 index 000000000..38b6b0ab9 --- /dev/null +++ b/api/app/tests/contracts/test_exam_contracts.py @@ -0,0 +1,118 @@ +from datetime import datetime, timedelta, timezone + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + future_utc_window, + json_of, + unique_name, +) +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import EXAM_LIST_RESPONSE_SCHEMA, EXAM_RESPONSE_SCHEMA + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def _create_booking(api_client, seeded_data, *, days_from_now): + start_time, end_time = future_utc_window(days_from_now, duration_minutes=120) + response = api_client.post( + "/bookings/", + json={ + "booking_name": unique_name("booking"), + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": seeded_data["office_ids"]["test_office"], + "room_id": seeded_data["room_id"], + "start_time": start_time, + "end_time": end_time, + "invigilator_id": [seeded_data["invigilator_ids"][0]], + }, + ) + assert_json_response(response, 201) + return json_of(response)["booking"] + + +def _create_exam(api_client, seeded_data, booking_id, *, event_id): + expiry_date = ( + (datetime.now(timezone.utc) + timedelta(days=30)) + .replace(microsecond=0) + .isoformat() + ) + response = api_client.post( + "/exams/", + json={ + "booking_id": booking_id, + "event_id": event_id, + "exam_method": "paper", + "exam_name": unique_name("exam"), + "exam_type_id": seeded_data["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Codex exam notes", + "number_of_students": 19, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": expiry_date, + }, + ) + assert_json_response(response, 201) + return json_of(response)["exam"] + + +def test_exam_create_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that exam creation returns the stable exam payload contract.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + response = internal_ga_client.post( + "/exams/", + json={ + "booking_id": booking["booking_id"], + "event_id": f"event-{booking['booking_id']}", + "exam_method": "paper", + "exam_name": unique_name("exam"), + "exam_type_id": seeded_data["exam_type_id"], + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Codex exam notes", + "number_of_students": 19, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": (datetime.now(timezone.utc) + timedelta(days=30)) + .replace(microsecond=0) + .isoformat(), + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + validate_schema(body, EXAM_RESPONSE_SCHEMA) + assert body["exam"]["booking_id"] == booking["booking_id"] + + +def test_exam_detail_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that exam detail preserves the contract used by exam detail screens.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id="event-detail" + ) + + response = internal_ga_client.get(f"/exams/{exam['exam_id']}/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, EXAM_RESPONSE_SCHEMA) + assert body["exam"]["exam_id"] == exam["exam_id"] + + +def test_exam_list_response_matches_the_contract(internal_ga_client, seeded_data): + """Assert that exam list items preserve the contract used by exam dashboards.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id="event-list" + ) + + response = internal_ga_client.get("/exams/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, EXAM_LIST_RESPONSE_SCHEMA) + assert any(item["exam_id"] == exam["exam_id"] for item in body["exams"]) diff --git a/api/app/tests/contracts/test_public_api_contracts.py b/api/app/tests/contracts/test_public_api_contracts.py new file mode 100644 index 000000000..e501b7368 --- /dev/null +++ b/api/app/tests/contracts/test_public_api_contracts.py @@ -0,0 +1,28 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import PUBLIC_USER_LIST_SCHEMA + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def test_public_user_create_response_matches_the_contract(public_client): + """Assert that public-user creation returns the stable user profile contract.""" + response = public_client.post("/users/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, PUBLIC_USER_LIST_SCHEMA) + assert body[0]["username"].endswith("@bceid") + + +def test_public_user_me_response_matches_the_contract(public_client): + """Assert that the public-user self endpoint preserves the stable user profile contract.""" + create_response = public_client.post("/users/") + assert_json_response(create_response, 200) + + response = public_client.get("/users/me/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, PUBLIC_USER_LIST_SCHEMA) diff --git a/api/app/tests/contracts/test_reference_data_contracts.py b/api/app/tests/contracts/test_reference_data_contracts.py new file mode 100644 index 000000000..78581859d --- /dev/null +++ b/api/app/tests/contracts/test_reference_data_contracts.py @@ -0,0 +1,346 @@ +from datetime import datetime, timezone + +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import ( + CATEGORY_LIST_RESPONSE_SCHEMA, + CSR_LIST_RESPONSE_SCHEMA, + CHANNEL_LIST_RESPONSE_SCHEMA, + CSR_ME_RESPONSE_SCHEMA, + EXAM_TYPE_LIST_RESPONSE_SCHEMA, + INVIGILATOR_LIST_RESPONSE_SCHEMA, + OFFICE_LIST_RESPONSE_SCHEMA, + ROOM_LIST_RESPONSE_SCHEMA, + SMARTBOARD_SIDE_MENU_RESPONSE_SCHEMA, + SERVICE_LIST_RESPONSE_SCHEMA, +) +from app.tests.auth.auth_support import promote_internal_csr_to_support + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def test_channels_contract_includes_named_channels(internal_ga_client): + """Assert that channels expose the stable identifier and display-name fields.""" + response = internal_ga_client.get("/channels/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CHANNEL_LIST_RESPONSE_SCHEMA) + assert any(channel["channel_name"] == "Phone" for channel in body["channels"]) + + +def test_categories_contract_includes_dashboard_flags(internal_ga_client): + """Assert that category responses preserve the frontend flag fields and parent reference.""" + response = internal_ga_client.get("/categories/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CATEGORY_LIST_RESPONSE_SCHEMA) + + property_tax = next( + category + for category in body["categories"] + if category["service_name"] == "Property Tax" + ) + assert property_tax["actual_service_ind"] == 0 + assert "display_dashboard_ind" in property_tax + assert property_tax["parent_id"] is None + + +def test_services_contract_includes_parent_relationships(internal_ga_client): + """Assert that services expose parent relationships needed for service selection UIs.""" + response = internal_ga_client.get("/services/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, SERVICE_LIST_RESPONSE_SCHEMA) + + service = next( + service + for service in body["services"] + if service["service_name"] == "Payment - MSP" + ) + assert service["parent_id"] is not None + assert service["parent"]["service_name"] + + +def test_office_scoped_services_filter_deleted_entries_and_preserve_sort_order( + internal_ga_client, seeded_data, app +): + """Assert that office-scoped services stay filtered and stably ordered for service-picking UIs.""" + with app.app_context(): + from app.models.theq import Office, Service + from qsystem import db + + test_office = Office.find_by_id(seeded_data["office_ids"]["test_office"]) + limited_office = Office.find_by_id(seeded_data["office_ids"]["limited_office"]) + + ordered_parent = Service( + service_code="ORDERED-CATEGORY", + service_name="Ordered Category", + service_desc="Ordered category for scoped services tests", + prefix="O", + display_dashboard_ind=0, + actual_service_ind=0, + ) + alpha_child = Service( + service_code="ORDERED-ALPHA", + service_name="Alpha Child", + service_desc="Alpha child service", + prefix="O", + display_dashboard_ind=1, + actual_service_ind=1, + parent=ordered_parent, + ) + zebra_child = Service( + service_code="ORDERED-ZEBRA", + service_name="Zebra Child", + service_desc="Zebra child service", + prefix="O", + display_dashboard_ind=1, + actual_service_ind=1, + parent=ordered_parent, + ) + deleted_child = Service( + service_code="ORDERED-DELETED", + service_name="Deleted Child", + service_desc="Deleted child service", + prefix="O", + display_dashboard_ind=1, + actual_service_ind=1, + parent=ordered_parent, + deleted=datetime.now(timezone.utc), + ) + other_office_only = Service( + service_code="ORDERED-OTHER", + service_name="Other Office Only", + service_desc="Service available only in the limited office", + prefix="O", + display_dashboard_ind=1, + actual_service_ind=1, + parent=ordered_parent, + ) + + db.session.add_all( + [ + ordered_parent, + alpha_child, + zebra_child, + deleted_child, + other_office_only, + ] + ) + db.session.flush() + + test_office.services.extend( + [ordered_parent, alpha_child, zebra_child, deleted_child] + ) + limited_office.services.append(other_office_only) + db.session.add_all([test_office, limited_office]) + db.session.commit() + + response = internal_ga_client.get( + f"/services/?office_id={seeded_data['office_ids']['test_office']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, SERVICE_LIST_RESPONSE_SCHEMA) + + service_names = [service["service_name"] for service in body["services"]] + assert "Deleted Child" not in service_names + assert "Other Office Only" not in service_names + assert service_names.index("Ordered Category") < service_names.index("Alpha Child") + assert service_names.index("Alpha Child") < service_names.index("Zebra Child") + + +def test_offices_contract_includes_timezone_counters_and_timeslots(internal_ga_client): + """Assert that office responses preserve nested timezone, counter, and timeslot data.""" + response = internal_ga_client.get("/offices/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, OFFICE_LIST_RESPONSE_SCHEMA) + + office = next( + office for office in body["offices"] if office["office_name"] == "Test Office" + ) + assert office["timezone"]["timezone_name"] + assert office["counters"] + assert "timeslots" in office + + +def test_rooms_contract_includes_room_metadata(internal_ga_client): + """Assert that room responses preserve the room metadata needed by booking screens.""" + response = internal_ga_client.get("/rooms/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, ROOM_LIST_RESPONSE_SCHEMA) + assert any(room["room_name"] == "Boardroom 1" for room in body["rooms"]) + + +def test_rooms_contract_can_be_scoped_to_an_office_and_excludes_deleted_rooms( + internal_ga_client, seeded_data, app +): + """Assert that room-list filtering keeps only active rooms for the selected office.""" + with app.app_context(): + from app.models.bookings import Room + from qsystem import db + + db.session.add_all( + [ + Room( + office_id=seeded_data["office_ids"]["test_office"], + room_name="Scoped Contract Room", + capacity=4, + color="#00AA66", + ), + Room( + office_id=seeded_data["office_ids"]["limited_office"], + room_name="Other Office Room", + capacity=4, + color="#AA6600", + ), + Room( + office_id=seeded_data["office_ids"]["test_office"], + room_name="Deleted Contract Room", + capacity=4, + color="#AA0066", + deleted=datetime.now(timezone.utc), + ), + ] + ) + db.session.commit() + + response = internal_ga_client.get( + f"/rooms/?office_id={seeded_data['office_ids']['test_office']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, ROOM_LIST_RESPONSE_SCHEMA) + + room_names = [room["room_name"] for room in body["rooms"]] + assert "Scoped Contract Room" in room_names + assert "Other Office Room" not in room_names + assert "Deleted Contract Room" not in room_names + + +def test_invigilators_contract_includes_shadow_flags(internal_ga_client): + """Assert that invigilator responses preserve shadow-count and contact fields.""" + response = internal_ga_client.get("/invigilators/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, INVIGILATOR_LIST_RESPONSE_SCHEMA) + + invigilator = next( + invigilator + for invigilator in body["invigilators"] + if invigilator["invigilator_name"] == "Homer Simpson" + ) + assert invigilator["shadow_count"] == 2 + assert invigilator["shadow_flag"] == "Y" + + +def test_invigilators_offsite_contract_is_scoped_to_the_pesticide_office( + internal_ga_client, seeded_data +): + """Assert that the offsite invigilator payload stays limited to the pesticide office.""" + response = internal_ga_client.get("/invigilators/offsite/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, INVIGILATOR_LIST_RESPONSE_SCHEMA) + + names = {invigilator["invigilator_name"] for invigilator in body["invigilators"]} + assert names >= {"Pest 1", "Pest 2"} + assert "Homer Simpson" not in names + assert all( + invigilator["office_id"] == seeded_data["office_ids"]["pesticide_office"] + for invigilator in body["invigilators"] + ) + + +def test_exam_types_contract_includes_duration_and_group_flags(internal_ga_client): + """Assert that exam-type responses preserve duration and grouping metadata.""" + response = internal_ga_client.get("/exam_types/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, EXAM_TYPE_LIST_RESPONSE_SCHEMA) + assert any(exam_type["exam_type_name"] for exam_type in body["exam_types"]) + + +def test_csrs_contract_filters_deleted_users(internal_ga_client, app): + """Assert that the CSR list keeps stable fields while excluding deleted users.""" + with app.app_context(): + from app.models.theq import CSR + from qsystem import db + + csr = CSR.query.filter_by(username="cfms-postman-non-operator").first() + csr.deleted = datetime.now(timezone.utc) + db.session.add(csr) + db.session.commit() + + response = internal_ga_client.get("/csrs/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CSR_LIST_RESPONSE_SCHEMA) + + usernames = {csr["username"] for csr in body["csrs"]} + assert "cfms-postman-operator" in usernames + assert "cfms-postman-non-operator" not in usernames + + +def test_csrs_contract_allows_support_but_rejects_other_internal_roles( + internal_ga_client, internal_nonqtxn_client, app +): + """Assert that only GA and SUPPORT users can reach the office CSR list.""" + forbidden_response = internal_nonqtxn_client.get("/csrs/") + assert forbidden_response.status_code == 403 + + promote_internal_csr_to_support(app, username="cfms-postman-operator") + response = internal_ga_client.get("/csrs/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CSR_LIST_RESPONSE_SCHEMA) + assert any(csr["role"]["role_code"] == "SUPPORT" for csr in body["csrs"]) + + +def test_csr_me_contract_includes_role_office_and_designate_flags(internal_ga_client): + """Assert that the csr-self payload preserves nested office and designate flag fields.""" + response = internal_ga_client.get("/csrs/me/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, CSR_ME_RESPONSE_SCHEMA) + + csr = body["csr"] + assert csr["role"]["role_code"] == "GA" + assert csr["office"]["office_name"] == "Test Office" + assert "finance_designate" in csr + assert "ita2_designate" in csr + assert "pesticide_designate" in csr + + +def test_smartboard_side_menu_contract_returns_the_frontend_office_payload( + bare_client, seeded_data +): + """Assert that the smartboard side menu keeps returning the nested office payload the UI consumes.""" + response = bare_client.get( + f"/smardboard/side-menu/{seeded_data['office_numbers']['test_office']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, SMARTBOARD_SIDE_MENU_RESPONSE_SCHEMA) + + office = body["office"] + assert office["office_number"] == seeded_data["office_numbers"]["test_office"] + assert office["timezone"]["timezone_name"] == seeded_data["office_timezones"]["test_office"] + assert "currently_waiting" in office diff --git a/api/app/tests/contracts/test_reminder_contracts.py b/api/app/tests/contracts/test_reminder_contracts.py new file mode 100644 index 000000000..f999fd2d8 --- /dev/null +++ b/api/app/tests/contracts/test_reminder_contracts.py @@ -0,0 +1,64 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.auth.auth_support import create_public_appointment +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import REMINDER_RESPONSE_SCHEMA +from app.tests.helpers.appointments import ( + align_current_pacific_time_for_appointment, + configure_public_user_reminders, + create_internal_reminder_appointment, +) + +pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] + + +def test_email_reminder_response_matches_the_contract( + internal_ga_client, monkeypatch, reminder_job_client, seeded_data +): + """Assert that email reminders return the stable payload contract for anonymous appointments.""" + appointment = create_internal_reminder_appointment( + internal_ga_client, + seeded_data, + contact_information="anonymous@example.com", + ) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["test_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/email/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, REMINDER_RESPONSE_SCHEMA) + assert any(item["email"] == "anonymous@example.com" for item in body["appointments"]) + + +def test_sms_reminder_response_matches_the_contract( + monkeypatch, public_client, reminder_job_client, seeded_data +): + """Assert that SMS reminders return the stable payload contract for opted-in public users.""" + user = configure_public_user_reminders( + public_client, + email="public@example.com", + telephone="2505550100", + send_sms_reminders=True, + ) + appointment = create_public_appointment(public_client, seeded_data) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["limited_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/sms/") + body = json_of(response) + + assert_json_response(response, 200) + validate_schema(body, REMINDER_RESPONSE_SCHEMA) + assert any( + item["display_name"] == user["display_name"] + and item["user_telephone"] == "2505550100" + for item in body["appointments"] + ) diff --git a/api/app/tests/fixtures/__init__.py b/api/app/tests/fixtures/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/api/app/tests/fixtures/__init__.py @@ -0,0 +1 @@ + diff --git a/api/app/tests/fixtures/auth.py b/api/app/tests/fixtures/auth.py new file mode 100644 index 000000000..dbc5da078 --- /dev/null +++ b/api/app/tests/fixtures/auth.py @@ -0,0 +1,41 @@ +import pytest +from app.tests.api_test_support import ApiClient + + +@pytest.fixture() +def api_client_factory(app): + def factory(identity_name=None, token="theq-test-token"): + return ApiClient(app.test_client(), identity_name, token) + + return factory + + +@pytest.fixture() +def internal_ga_client(api_client_factory): + return api_client_factory("internal_ga") + + +@pytest.fixture() +def internal_nonqtxn_client(api_client_factory): + return api_client_factory("internal_nonqtxn") + + +@pytest.fixture() +def public_client(api_client_factory): + return api_client_factory("public_user") + + +@pytest.fixture() +def public_client_alt(api_client_factory): + return api_client_factory("public_user_alt") + + +@pytest.fixture() +def reminder_job_client(api_client_factory): + return api_client_factory("reminder_job") + + +@pytest.fixture() +def bare_client(api_client_factory): + return api_client_factory(identity_name=None, token=None) + diff --git a/api/app/tests/fixtures/db.py b/api/app/tests/fixtures/db.py new file mode 100644 index 000000000..684a48419 --- /dev/null +++ b/api/app/tests/fixtures/db.py @@ -0,0 +1,474 @@ +import copy +import importlib +import os +import sys +import uuid +from contextlib import closing +from functools import wraps + +import pytest + +try: + import psycopg2 + from psycopg2 import sql +except ImportError: # pragma: no cover - handled by session skip + psycopg2 = None + sql = None + + +TEST_IDENTITIES = { + "internal_ga": { + "username": "cfms-postman-operator", + "identity_provider": "idir", + "realm_access": {"roles": ["internal_user"]}, + "email": "ga@example.com", + "display_name": "GA User", + "family_name": "GA", + }, + "internal_nonqtxn": { + "username": "cfms-postman-non-operator", + "identity_provider": "idir", + "realm_access": {"roles": ["internal_user"]}, + "email": "csr@example.com", + "display_name": "CSR User", + "family_name": "CSR", + }, + "public_user": { + "username": "theq-public-user", + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public@example.com", + "display_name": "Public User", + "family_name": "Public", + }, + "public_user_alt": { + "username": "theq-public-user-alt", + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public-alt@example.com", + "display_name": "Public Alt User", + "family_name": "PublicAlt", + }, + "reminder_job": { + "username": "theq-reminder-job", + "identity_provider": "idir", + "realm_access": {"roles": ["reminder_job"]}, + "email": "reminder-job@example.com", + "display_name": "Reminder Job", + "family_name": "Reminder", + }, +} + + +def _db_settings(): + return { + "engine": os.getenv("TEST_DATABASE_ENGINE", "postgresql+psycopg2"), + "host": os.getenv( + "TEST_DATABASE_HOST", os.getenv("DATABASE_HOST", "127.0.0.1") + ), + "port": os.getenv("TEST_DATABASE_PORT", os.getenv("DATABASE_PORT", "5432")), + "user": os.getenv( + "TEST_DATABASE_USERNAME", os.getenv("DATABASE_USERNAME", "postgres") + ), + "password": os.getenv( + "TEST_DATABASE_PASSWORD", os.getenv("DATABASE_PASSWORD", "root") + ), + "admin_db": os.getenv("TEST_DATABASE_ADMIN_DB", "postgres"), + } + + +def _connect(database_name): + settings = _db_settings() + return psycopg2.connect( + dbname=database_name, + user=settings["user"], + password=settings["password"], + host=settings["host"], + port=settings["port"], + ) + + +def _skip_or_exit_for_missing_db(pytestconfig, message): + if pytestconfig.getoption("--require-integration-db"): + pytest.exit(message, returncode=2) + pytest.skip(message) + + +def _patch_jwt_manager(): + from flask import abort, g + from flask_jwt_oidc import JwtManager + + if getattr(JwtManager, "_theq_pytest_patched", False): + return + + def _require_identity(): + token_info = getattr(g, "jwt_oidc_token_info", None) + if token_info is None: + abort(401) + return token_info + + def _wrap_with_auth(func, roles=None): + @wraps(func) + def wrapped(*args, **kwargs): + token_info = _require_identity() + if roles is not None: + token_roles = token_info.get("realm_access", {}).get("roles", []) + if not any(role in token_roles for role in roles): + abort(403) + return func(*args, **kwargs) + + return wrapped + + def requires_auth(self, *decorator_args, **decorator_kwargs): + del self, decorator_kwargs + if decorator_args and callable(decorator_args[0]) and len(decorator_args) == 1: + return _wrap_with_auth(decorator_args[0]) + + def decorator(func): + return _wrap_with_auth(func) + + return decorator + + def has_one_of_roles(self, roles): + del self + + def decorator(func): + return _wrap_with_auth(func, roles=roles) + + return decorator + + def init_app(self, app): + app.extensions["theq_test_jwt"] = self + + JwtManager.has_one_of_roles = has_one_of_roles + JwtManager.requires_auth = requires_auth + JwtManager.requires_auth_cookie = requires_auth + JwtManager.init_app = init_app + JwtManager._theq_pytest_patched = True + + +def _install_identity_loader(app): + if app.config.get("THEQ_TEST_IDENTITY_LOADER"): + return + + from flask import g, request + + app.config["TEST_IDENTITIES"] = TEST_IDENTITIES + app.config["DISABLE_AUTO_REFRESH"] = True + + @app.before_request + def _load_theq_test_identity(): + identity_name = request.headers.get("X-TheQ-Test-Identity") + identity = app.config["TEST_IDENTITIES"].get(identity_name) + if identity: + g.jwt_oidc_token_info = copy.deepcopy(identity) + + app.config["THEQ_TEST_IDENTITY_LOADER"] = True + + +def _stub_integrations(): + import qsystem + from app.utilities.snowplow import SnowPlow + + qsystem.socketio.emit = lambda *args, **kwargs: None + + for method_name in ( + "add_citizen", + "choose_service", + "snowplow_event", + "snowplow_appointment", + ): + setattr(SnowPlow, method_name, staticmethod(lambda *args, **kwargs: None)) + + def email_stub(*args, **kwargs): + return None + + def email_contents_stub(*args, **kwargs): + return ("test@example.com", "Subject", "Body") + + def sms_stub(*args, **kwargs): + return True + + patch_targets = { + "app.resources.theq.citizen.citizen_add_to_queue": { + "send_email": email_stub, + "get_walkin_spot_confirmation_email_contents": email_contents_stub, + "send_walkin_spot_confirmation_sms": sms_stub, + }, + "app.resources.theq.citizen.citizen_detail": { + "send_email": email_stub, + "get_walkin_reminder_email_contents": email_contents_stub, + "send_walkin_reminder_sms": sms_stub, + }, + "app.resources.theq.user.user": { + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_post": { + "send_email": email_stub, + "get_confirmation_email_contents": email_contents_stub, + "get_blackout_email_contents": email_contents_stub, + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_put": { + "send_email": email_stub, + "get_confirmation_email_contents": email_contents_stub, + "send_sms": sms_stub, + }, + "app.resources.bookings.appointment.appointment_delete": { + "send_email": email_stub, + "get_cancel_email_contents": email_contents_stub, + }, + } + + for module_name, attributes in patch_targets.items(): + module = importlib.import_module(module_name) + for attribute_name, value in attributes.items(): + setattr(module, attribute_name, value) + + +@pytest.fixture(scope="session") +def postgres_database(pytestconfig): + if psycopg2 is None: + _skip_or_exit_for_missing_db( + pytestconfig, "psycopg2 is required for the integration suite" + ) + + database_name = f"qsystem_sqlalchemy_smoke_{uuid.uuid4().hex[:12]}" + settings = _db_settings() + + try: + with closing(_connect(settings["admin_db"])) as connection: + connection.autocommit = True + with connection.cursor() as cursor: + cursor.execute( + sql.SQL("CREATE DATABASE {}").format(sql.Identifier(database_name)) + ) + except Exception as exc: # pragma: no cover - depends on local services + _skip_or_exit_for_missing_db( + pytestconfig, f"unable to create disposable Postgres database: {exc}" + ) + + original_env = { + key: os.environ.get(key) + for key in ( + "FLASK_CONFIGURATION", + "DATABASE_ENGINE", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_USERNAME", + "DATABASE_PASSWORD", + "DATABASE_NAME", + "JWT_OIDC_WELL_KNOWN_CONFIG", + "JWT_OIDC_JWKS_URI", + "JWT_OIDC_ISSUER", + "JWT_OIDC_AUDIENCE", + ) + } + + os.environ.update( + { + "FLASK_CONFIGURATION": "localhost", + "DATABASE_ENGINE": settings["engine"], + "DATABASE_HOST": settings["host"], + "DATABASE_PORT": settings["port"], + "DATABASE_USERNAME": settings["user"], + "DATABASE_PASSWORD": settings["password"], + "DATABASE_NAME": database_name, + "JWT_OIDC_WELL_KNOWN_CONFIG": "", + "JWT_OIDC_JWKS_URI": "https://example.com/jwks.json", + "JWT_OIDC_ISSUER": "https://example.com/", + "JWT_OIDC_AUDIENCE": "queue-api-tests", + } + ) + + try: + yield { + "database_name": database_name, + "database_uri": ( + f"{settings['engine']}://{settings['user']}:{settings['password']}" + f"@{settings['host']}:{settings['port']}/{database_name}" + ), + } + finally: + for module_name in ("manage", "qsystem"): + module = sys.modules.get(module_name) + if module is not None and hasattr(module, "db"): + try: + module.db.session.remove() + if hasattr(module, "application"): + with module.application.app_context(): + module.db.engine.dispose() + except Exception: + pass + + for module_name in list(sys.modules): + if ( + module_name == "app" + or module_name.startswith("app.") + or module_name in ("manage", "qsystem") + ): + sys.modules.pop(module_name, None) + + try: + with closing(_connect(settings["admin_db"])) as connection: + connection.autocommit = True + with connection.cursor() as cursor: + cursor.execute( + sql.SQL( + "SELECT pg_terminate_backend(pid) " + "FROM pg_stat_activity WHERE datname = %s AND pid <> pg_backend_pid()" + ), + (database_name,), + ) + cursor.execute( + sql.SQL("DROP DATABASE IF EXISTS {}").format( + sql.Identifier(database_name) + ) + ) + finally: + for key, value in original_env.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + +@pytest.fixture(scope="session") +def app_module(postgres_database): + del postgres_database + _patch_jwt_manager() + module = importlib.import_module("manage") + _install_identity_loader(module.application) + _stub_integrations() + return module + + +@pytest.fixture(scope="session") +def app(app_module): + return app_module.application + + +@pytest.fixture(scope="session") +def db(app_module): + return app_module.db + + +@pytest.fixture(scope="session") +def cli_runner(app): + return app.test_cli_runner() + + +@pytest.fixture(scope="session") +def client(app): + return app.test_client() + + +@pytest.fixture(scope="session") +def migrated_database(cli_runner): + result = cli_runner.invoke(args=["db", "upgrade"]) + assert result.exit_code == 0, result.output + return result + + +@pytest.fixture() +def seeded_database(cli_runner, migrated_database, app): + del migrated_database + + with app.app_context(): + from qsystem import db + + db.session.remove() + + result = cli_runner.invoke(args=["bootstrap"]) + assert result.exit_code == 0, result.output + + with app.app_context(): + from qsystem import cache, db + + db.session.remove() + cache.clear() + + return result + + +@pytest.fixture() +def seeded_data(seeded_database, app): + del seeded_database + + with app.app_context(): + from app.models.bookings import ExamType, Invigilator, Room + from app.models.theq import CSR, Channel, Counter, Office, Service + + office_test = Office.query.filter_by(office_name="Test Office").first() + office_limited = Office.query.filter_by(office_name="100 Mile House").first() + office_victoria = Office.query.filter_by(office_name="Victoria").first() + office_pesticide = Office.query.filter_by( + office_name="Pesticide Offsite" + ).first() + quick_trans = Counter.query.filter_by(counter_name="Quick Trans").first() + counter = Counter.query.filter_by(counter_name="Counter").first() + phone_channel = Channel.query.filter_by(channel_name="Phone").first() + email_channel = Channel.query.filter_by(channel_name="Email/Fax/Mail").first() + msp_service = Service.query.filter_by(service_name="Payment - MSP").first() + ptax_service = Service.query.filter_by(service_name="Other - PTAX").first() + ptax_category = Service.query.filter_by(service_name="Property Tax").first() + dlkt_service = Service.query.filter_by( + service_name="Knowledge Test Set-Up/Result" + ).first() + limited_office_service = Service.query.filter_by( + service_name="Deferment Application" + ).first() + room = Room.query.filter_by(room_name="Boardroom 1").first() + invigilators = ( + Invigilator.query.filter_by(office_id=office_test.office_id) + .order_by(Invigilator.invigilator_id) + .all() + ) + exam_type = ExamType.query.order_by(ExamType.exam_type_id).first() + ga = CSR.query.filter_by(username="cfms-postman-operator").first() + non_qtxn = CSR.query.filter_by(username="cfms-postman-non-operator").first() + + return { + "office_ids": { + "test_office": office_test.office_id, + "limited_office": office_limited.office_id, + "victoria": office_victoria.office_id, + "pesticide_office": office_pesticide.office_id, + }, + "office_numbers": { + "test_office": office_test.office_number, + "limited_office": office_limited.office_number, + "victoria": office_victoria.office_number, + "pesticide_office": office_pesticide.office_number, + }, + "office_timezones": { + "test_office": office_test.timezone.timezone_name, + "limited_office": office_limited.timezone.timezone_name, + "victoria": office_victoria.timezone.timezone_name, + "pesticide_office": office_pesticide.timezone.timezone_name, + }, + "counter_ids": { + "quick_trans": quick_trans.counter_id, + "counter": counter.counter_id, + }, + "channel_ids": { + "phone": phone_channel.channel_id, + "email": email_channel.channel_id, + }, + "service_ids": { + "msp": msp_service.service_id, + "ptax": ptax_service.service_id, + "ptax_category": ptax_category.service_id, + "dlkt": dlkt_service.service_id, + "limited_office_service": limited_office_service.service_id, + }, + "csr_ids": { + "ga": ga.csr_id, + "non_qtxn": non_qtxn.csr_id, + }, + "room_id": room.room_id, + "invigilator_ids": [ + invigilator.invigilator_id for invigilator in invigilators + ], + "exam_type_id": exam_type.exam_type_id, + } diff --git a/api/app/tests/fixtures/smoke.py b/api/app/tests/fixtures/smoke.py new file mode 100644 index 000000000..c1bec85a4 --- /dev/null +++ b/api/app/tests/fixtures/smoke.py @@ -0,0 +1,10 @@ +from flask import Flask +import pytest + + +@pytest.fixture(scope="session") +def minimal_app(): + app = Flask(__name__) + app.config["TESTING"] = True + return app + diff --git a/api/app/tests/flows/__init__.py b/api/app/tests/flows/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/api/app/tests/flows/__init__.py @@ -0,0 +1 @@ + diff --git a/api/app/tests/flows/test_appointment_flows.py b/api/app/tests/flows/test_appointment_flows.py new file mode 100644 index 000000000..ad761d31e --- /dev/null +++ b/api/app/tests/flows/test_appointment_flows.py @@ -0,0 +1,252 @@ +from __future__ import annotations + +from uuid import uuid4 + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_public_user, + first_day_with_slots, + json_of, + public_slot_payload, + slot_window_to_iso, +) +from app.tests.api_test_support import ( + create_internal_appointment as _create_internal_appointment, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def test_internal_appointment_can_be_listed_and_retrieved( + internal_ga_client, seeded_data +): + """Assert that created internal appointments appear in list and detail responses.""" + appointment = _create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + list_response = internal_ga_client.get("/appointments/") + detail_response = internal_ga_client.get( + f"/appointments/{appointment['appointment_id']}/" + ) + + assert_json_response(list_response, 200) + assert_json_response(detail_response, 200) + assert any( + item["appointment_id"] == appointment["appointment_id"] + for item in json_of(list_response)["appointments"] + ) + assert ( + json_of(detail_response)["appointment"]["appointment_id"] + == appointment["appointment_id"] + ) + + +def test_internal_appointment_can_be_updated(internal_ga_client, seeded_data): + """Assert that internal appointments preserve comment updates.""" + appointment = _create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.put( + f"/appointments/{appointment['appointment_id']}/", + json={"comments": "Internal appointment updated"}, + ) + + assert_json_response(response, 200) + assert ( + json_of(response)["appointment"]["comments"] == "Internal appointment updated" + ) + + +def test_internal_appointment_can_be_deleted(internal_ga_client, seeded_data): + """Assert that internal appointments can be deleted from the API.""" + appointment = _create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + response = internal_ga_client.delete( + f"/appointments/{appointment['appointment_id']}/" + ) + + assert response.status_code == 204, response.get_data(as_text=True) + + +def test_recurring_appointment_update_applies_to_each_occurrence( + internal_ga_client, seeded_data +): + """Assert that recurring appointment updates apply across all matching occurrences.""" + recurring_uuid = str(uuid4()) + first_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=3, + recurring_uuid=recurring_uuid, + ) + second_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=4, + recurring_uuid=recurring_uuid, + ) + + recurring_update = internal_ga_client.put( + f"/appointments/recurring/{recurring_uuid}", + json={"comments": "Recurring appointment updated"}, + ) + final_list = internal_ga_client.get("/appointments/") + appointments = { + item["appointment_id"]: item for item in json_of(final_list)["appointments"] + } + + assert_json_response(recurring_update, 200) + assert_json_response(final_list, 200) + assert ( + appointments[first_recurring["appointment_id"]]["comments"] + == "Recurring appointment updated" + ) + assert ( + appointments[second_recurring["appointment_id"]]["comments"] + == "Recurring appointment updated" + ) + + +def test_recurring_appointment_delete_removes_each_occurrence( + internal_ga_client, seeded_data +): + """Assert that recurring appointment deletion removes every matching occurrence.""" + recurring_uuid = str(uuid4()) + first_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=3, + recurring_uuid=recurring_uuid, + ) + second_recurring = _create_internal_appointment( + internal_ga_client, + seeded_data, + days_from_now=4, + recurring_uuid=recurring_uuid, + ) + + recurring_delete = internal_ga_client.delete( + f"/appointments/recurring/{recurring_uuid}" + ) + final_list = internal_ga_client.get("/appointments/") + remaining_ids = { + item["appointment_id"] for item in json_of(final_list)["appointments"] + } + + assert recurring_delete.status_code == 204, recurring_delete.get_data(as_text=True) + assert_json_response(final_list, 200) + assert first_recurring["appointment_id"] not in remaining_ids + assert second_recurring["appointment_id"] not in remaining_ids + + +def test_public_user_profile_can_be_created_and_updated(public_client): + """Assert that public-user profiles can be created and updated before booking.""" + created_user = create_public_user(public_client) + + update_user_response = public_client.put( + f"/users/{created_user['user_id']}/", + json={ + "email": "updated-public@example.com", + "telephone": "2505550100", + "send_email_reminders": True, + "send_sms_reminders": True, + }, + ) + get_me_response = public_client.get("/users/me/") + + assert_json_response(update_user_response, 200) + assert_json_response(get_me_response, 200) + assert json_of(update_user_response)[0]["email"] == "updated-public@example.com" + + +def test_public_user_appointment_appears_in_their_appointment_list( + public_client, seeded_data +): + """Assert that public bookings appear in the authenticated user's appointment list.""" + create_public_user(public_client) + + appointment_payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + create_appointment_response = public_client.post( + "/appointments/", json=appointment_payload + ) + assert_json_response(create_appointment_response, 201) + appointment = json_of(create_appointment_response)["appointment"] + + list_response = public_client.get("/users/appointments/") + + assert_json_response(list_response, 200) + assert any( + item["appointment_id"] == appointment["appointment_id"] + for item in json_of(list_response)["appointments"] + ) + + +def test_public_user_appointment_limit_rejects_a_second_booking( + public_client, seeded_data +): + """Assert that the public appointment limit rejects a second booking on the same day.""" + create_public_user(public_client) + + appointment_payload, day_key, slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + first_response = public_client.post("/appointments/", json=appointment_payload) + assert_json_response(first_response, 201) + + second_start_time, second_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + max_limit_response = public_client.post( + "/appointments/", + json={ + **appointment_payload, + "start_time": second_start_time, + "end_time": second_end_time, + }, + ) + + assert_json_response(max_limit_response, 400) + assert json_of(max_limit_response)["code"] == "MAX_NO_OF_APPOINTMENTS_REACHED" + + +def test_public_user_can_cancel_their_appointment(public_client, seeded_data): + """Assert that public users can cancel their own appointment bookings.""" + create_public_user(public_client) + + appointment_payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=1 + ) + create_appointment_response = public_client.post( + "/appointments/", json=appointment_payload + ) + assert_json_response(create_appointment_response, 201) + appointment = json_of(create_appointment_response)["appointment"] + + delete_response = public_client.delete( + f"/appointments/{appointment['appointment_id']}/" + ) + + assert delete_response.status_code == 204, delete_response.get_data(as_text=True) + + +def test_appointment_slots_endpoint_returns_available_capacity( + public_client, seeded_data +): + """Assert that the slots endpoint exposes at least one available slot with capacity metadata.""" + response = public_client.get( + f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" + ) + + assert_json_response(response, 200) + day_key, slots = first_day_with_slots(json_of(response)) + assert day_key + assert slots[0]["no_of_slots"] >= 1 diff --git a/api/app/tests/flows/test_booking_exam_flows.py b/api/app/tests/flows/test_booking_exam_flows.py new file mode 100644 index 000000000..700a21ea5 --- /dev/null +++ b/api/app/tests/flows/test_booking_exam_flows.py @@ -0,0 +1,272 @@ +from __future__ import annotations + +from datetime import datetime +from uuid import uuid4 +from zoneinfo import ZoneInfo + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + json_of, +) +from app.tests.api_test_support import ( + create_booking as _create_booking, +) +from app.tests.api_test_support import ( + create_exam as _create_exam, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def test_booking_can_be_listed_and_retrieved(internal_ga_client, seeded_data): + """Assert that created bookings appear in list and detail responses.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + list_response = internal_ga_client.get("/bookings/") + detail_response = internal_ga_client.get(f"/bookings/{booking['booking_id']}/") + + assert_json_response(list_response, 200) + assert_json_response(detail_response, 200) + assert any( + item["booking_id"] == booking["booking_id"] + for item in json_of(list_response)["bookings"] + ) + assert json_of(detail_response)["booking"]["booking_id"] == booking["booking_id"] + + +def test_booking_can_be_updated(internal_ga_client, seeded_data): + """Assert that booking updates preserve editable booking fields.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + response = internal_ga_client.put( + f"/bookings/{booking['booking_id']}/", + json={ + "booking_name": "Updated single booking", + "booking_contact_information": "updated-booking@example.com", + "invigilator_id": [seeded_data["invigilator_ids"][1]], + }, + ) + + assert_json_response(response, 200) + assert json_of(response)["booking"]["booking_name"] == "Updated single booking" + + +def test_booking_can_be_deleted(internal_ga_client, seeded_data): + """Assert that bookings can be deleted from the API.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) + + response = internal_ga_client.delete(f"/bookings/{booking['booking_id']}/") + + assert response.status_code == 204, response.get_data(as_text=True) + + +def test_recurring_booking_update_applies_to_each_occurrence( + internal_ga_client, seeded_data +): + """Assert that recurring booking updates apply to every matching occurrence.""" + recurring_uuid = str(uuid4()) + first_recurring = _create_booking( + internal_ga_client, + seeded_data, + days_from_now=3, + recurring_uuid=recurring_uuid, + ) + second_recurring = _create_booking( + internal_ga_client, + seeded_data, + days_from_now=4, + recurring_uuid=recurring_uuid, + ) + + recurring_update = internal_ga_client.put( + f"/bookings/recurring/{recurring_uuid}", + json={"booking_name": "Recurring booking updated"}, + ) + final_list = internal_ga_client.get("/bookings/") + bookings = {item["booking_id"]: item for item in json_of(final_list)["bookings"]} + + assert_json_response(recurring_update, 200) + assert_json_response(final_list, 200) + assert ( + bookings[first_recurring["booking_id"]]["booking_name"] + == "Recurring booking updated" + ) + assert ( + bookings[second_recurring["booking_id"]]["booking_name"] + == "Recurring booking updated" + ) + + +def test_recurring_booking_delete_removes_each_occurrence( + internal_ga_client, seeded_data +): + """Assert that recurring booking deletion removes every matching occurrence.""" + recurring_uuid = str(uuid4()) + first_recurring = _create_booking( + internal_ga_client, + seeded_data, + days_from_now=3, + recurring_uuid=recurring_uuid, + ) + second_recurring = _create_booking( + internal_ga_client, + seeded_data, + days_from_now=4, + recurring_uuid=recurring_uuid, + ) + + recurring_delete = internal_ga_client.delete( + f"/bookings/recurring/{recurring_uuid}" + ) + final_list = internal_ga_client.get("/bookings/") + final_booking_ids = {item["booking_id"] for item in json_of(final_list)["bookings"]} + + assert recurring_delete.status_code == 204, recurring_delete.get_data(as_text=True) + assert_json_response(final_list, 200) + assert first_recurring["booking_id"] not in final_booking_ids + assert second_recurring["booking_id"] not in final_booking_ids + + +def test_exam_can_be_listed_and_retrieved(internal_ga_client, seeded_data): + """Assert that created exams appear in list and detail responses.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id="event-detail" + ) + + detail_response = internal_ga_client.get(f"/exams/{exam['exam_id']}/") + list_response = internal_ga_client.get("/exams/") + + assert_json_response(detail_response, 200) + assert_json_response(list_response, 200) + assert json_of(detail_response)["exam"]["exam_id"] == exam["exam_id"] + assert any( + item["exam_id"] == exam["exam_id"] for item in json_of(list_response)["exams"] + ) + + +def test_exam_can_be_updated(internal_ga_client, seeded_data): + """Assert that exam updates preserve editable exam fields.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id="event-update" + ) + + response = internal_ga_client.put( + f"/exams/{exam['exam_id']}/", + json={"exam_name": "Updated exam name", "notes": "Updated notes"}, + ) + + assert_json_response(response, 201) + assert json_of(response)["exam"]["exam_name"] == "Updated exam name" + + +def test_exam_event_lookup_returns_the_matching_exam(internal_ga_client, seeded_data): + """Assert that exam event lookups continue to find the matching exam.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + event_id = str(9000 + booking["booking_id"]) + _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id=event_id + ) + + response = internal_ga_client.get(f"/exams/event_id/{event_id}/") + + assert_json_response(response, 200) + assert json_of(response)["message"] is True + + +def test_exam_export_contains_the_updated_exam(internal_ga_client, seeded_data): + """Assert that exam CSV exports include updated exam data for the requested office day.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + event_id = str(9000 + booking["booking_id"]) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id=event_id + ) + update_response = internal_ga_client.put( + f"/exams/{exam['exam_id']}/", + json={"exam_name": "Updated exam name", "notes": "Updated notes"}, + ) + assert_json_response(update_response, 201) + + booking_start = datetime.fromisoformat(booking["start_time"].replace("Z", "+00:00")) + export_date = ( + booking_start.astimezone( + ZoneInfo(seeded_data["office_timezones"]["test_office"]) + ) + .date() + .isoformat() + ) + export_response = internal_ga_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}" + ) + + assert export_response.status_code == 200, export_response.get_data(as_text=True) + export_body = export_response.get_data(as_text=True) + assert "Office Name,Exam Type,Exam ID,Exam Name" in export_body + assert "Updated exam name" in export_body + + +def test_exam_can_be_deleted(internal_ga_client, seeded_data): + """Assert that exams can be deleted from the API.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, seeded_data, booking["booking_id"], event_id="event-delete" + ) + + delete_response = internal_ga_client.delete(f"/exams/{exam['exam_id']}/") + post_delete_list = internal_ga_client.get("/exams/") + + assert delete_response.status_code == 204, delete_response.get_data(as_text=True) + assert_json_response(post_delete_list, 200) + assert all( + item["exam_id"] != exam["exam_id"] + for item in json_of(post_delete_list)["exams"] + ) + + +def test_invigilator_shadow_count_decrements(internal_ga_client): + """Assert that subtracting an invigilator shadow count preserves the remaining metadata.""" + list_response = internal_ga_client.get("/invigilators/") + assert_json_response(list_response, 200) + + first_invigilator = json_of(list_response)["invigilators"][0] + subtract_response = internal_ga_client.put( + f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=True&add=False" + ) + + assert_json_response(subtract_response, 200) + subtracted = json_of(subtract_response)["invigilator"] + assert subtracted["invigilator_id"] == first_invigilator["invigilator_id"] + assert subtracted["contact_email"] == first_invigilator["contact_email"] + assert subtracted["contact_phone"] == first_invigilator["contact_phone"] + assert subtracted["invigilator_notes"] == first_invigilator["invigilator_notes"] + assert subtracted["shadow_count"] == 1 + assert subtracted["shadow_flag"] == "N" + + +def test_invigilator_shadow_count_increment_restores_the_original_value( + internal_ga_client, +): + """Assert that adding back a shadow count restores the original invigilator state.""" + list_response = internal_ga_client.get("/invigilators/") + assert_json_response(list_response, 200) + + first_invigilator = json_of(list_response)["invigilators"][0] + subtract_response = internal_ga_client.put( + f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=True&add=False" + ) + assert_json_response(subtract_response, 200) + + add_response = internal_ga_client.put( + f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=False&add=True" + ) + + assert_json_response(add_response, 200) + added_back = json_of(add_response)["invigilator"] + assert added_back["invigilator_id"] == first_invigilator["invigilator_id"] + assert added_back["contact_email"] == first_invigilator["contact_email"] + assert added_back["contact_phone"] == first_invigilator["contact_phone"] + assert added_back["invigilator_notes"] == first_invigilator["invigilator_notes"] + assert added_back["shadow_count"] == 2 + assert added_back["shadow_flag"] == "Y" diff --git a/api/app/tests/flows/test_booking_recurring_current_office_flows.py b/api/app/tests/flows/test_booking_recurring_current_office_flows.py new file mode 100644 index 000000000..72b2f389b --- /dev/null +++ b/api/app/tests/flows/test_booking_recurring_current_office_flows.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import uuid4 + +import pytest + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _freeze_recurring_delete_today(monkeypatch, frozen_today: datetime): + from app.resources.bookings.booking import ( + booking_recurring_stat_delete as recurring_delete_module, + ) + + class FrozenDateTime(datetime): + @classmethod + def today(cls): + return frozen_today + + monkeypatch.setattr(recurring_delete_module, "datetime", FrozenDateTime) + + +def _create_booking_occurrence( + app, + *, + office_id: int, + recurring_uuid: str, + start_time: datetime, + booking_name: str, +) -> int: + with app.app_context(): + from app.models.bookings import Booking + from qsystem import db + + booking = Booking( + office_id=office_id, + room_id=None, + start_time=start_time, + end_time=start_time + timedelta(hours=2), + fees="false", + booking_name=booking_name, + booking_contact_information="booking@example.com", + recurring_uuid=recurring_uuid, + ) + db.session.add(booking) + db.session.commit() + return booking.booking_id + + +def test_current_office_recurring_delete_preserves_before_5am_rows_and_other_offices( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that current-office recurring deletes only remove this office's non-exempt future rows.""" + _freeze_recurring_delete_today(monkeypatch, datetime(2024, 7, 10, 12, 0, 0)) + recurring_uuid = str(uuid4()) + + same_office_future_id = _create_booking_occurrence( + app, + office_id=seeded_data["office_ids"]["test_office"], + recurring_uuid=recurring_uuid, + start_time=datetime(2024, 7, 11, 17, 0, tzinfo=timezone.utc), + booking_name="Delete Me", + ) + same_office_before_5am_id = _create_booking_occurrence( + app, + office_id=seeded_data["office_ids"]["test_office"], + recurring_uuid=recurring_uuid, + start_time=datetime(2024, 7, 10, 5, 0, tzinfo=timezone.utc), + booking_name="Preserve Before 5am", + ) + other_office_future_id = _create_booking_occurrence( + app, + office_id=seeded_data["office_ids"]["victoria"], + recurring_uuid=recurring_uuid, + start_time=datetime(2024, 7, 11, 18, 0, tzinfo=timezone.utc), + booking_name="Other Office", + ) + + response = internal_ga_client.delete( + f"/bookings/recurring/current-office/{recurring_uuid}" + ) + + assert response.status_code == 204, response.get_data(as_text=True) + + with app.app_context(): + from app.models.bookings import Booking + + remaining_ids = { + booking.booking_id + for booking in Booking.query.filter_by(recurring_uuid=recurring_uuid).all() + } + + assert same_office_future_id not in remaining_ids + assert same_office_before_5am_id in remaining_ids + assert other_office_future_id in remaining_ids diff --git a/api/app/tests/flows/test_csr_dashboard_flows.py b/api/app/tests/flows/test_csr_dashboard_flows.py new file mode 100644 index 000000000..876a8927b --- /dev/null +++ b/api/app/tests/flows/test_csr_dashboard_flows.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +from datetime import timedelta +from uuid import uuid4 + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_booking, + create_exam, + create_service_ready_citizen, + json_of, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _exam_type_id( + app, + *, + group_exam_ind: int | None = None, + ita_ind: int | None = None, + pesticide_exam_ind: int | None = None, +): + with app.app_context(): + from app.models.bookings import ExamType + + query = ExamType.query + if group_exam_ind is not None: + query = query.filter_by(group_exam_ind=group_exam_ind) + if ita_ind is not None: + query = query.filter_by(ita_ind=ita_ind) + if pesticide_exam_ind is not None: + query = query.filter_by(pesticide_exam_ind=pesticide_exam_ind) + + exam_type = query.order_by(ExamType.exam_type_id).first() + assert exam_type is not None + return exam_type.exam_type_id + + +def test_csr_me_reports_active_citizens_for_the_logged_in_csr( + internal_ga_client, seeded_data +): + """Assert that csr-self lists in-progress citizens already being served by the caller.""" + citizen, _service_request = create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Dashboard Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + ) + begin_service = internal_ga_client.post( + f"/citizens/{citizen['citizen_id']}/begin_service/" + ) + assert_json_response(begin_service, 200) + + response = internal_ga_client.get("/csrs/me/") + body = json_of(response) + + assert_json_response(response, 200) + assert body["attention_needed"] is False + assert citizen["citizen_id"] in { + active_citizen["citizen_id"] for active_citizen in body["active_citizens"] + } + + +def test_csr_me_flags_attention_for_stale_individual_exams( + app, internal_ga_client, seeded_data +): + """Assert that overdue individual exams surface the exam-manager attention banner.""" + booking = create_booking(internal_ga_client, seeded_data, days_from_now=5) + create_exam( + internal_ga_client, + seeded_data, + booking["booking_id"], + event_id=f"stale-{uuid4().hex[:8]}", + exam_type_id=_exam_type_id(app, group_exam_ind=0, ita_ind=1), + ) + + with app.app_context(): + from app.models.bookings import Booking + from qsystem import db + + booking_model = Booking.query.filter_by(booking_id=booking["booking_id"]).first() + duration = booking_model.end_time - booking_model.start_time + booking_model.start_time = booking_model.start_time - timedelta(days=10) + booking_model.end_time = booking_model.start_time + duration + db.session.add(booking_model) + db.session.commit() + + response = internal_ga_client.get("/csrs/me/") + + assert_json_response(response, 200) + assert json_of(response)["attention_needed"] is True + + +def test_csr_me_flags_attention_for_missing_pesticide_receipts( + app, internal_ga_client, seeded_data +): + """Assert that pesticide exams without a received date keep the dashboard alert active.""" + booking = create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = create_exam( + internal_ga_client, + seeded_data, + booking["booking_id"], + event_id=f"pest-{uuid4().hex[:8]}", + exam_type_id=_exam_type_id( + app, group_exam_ind=0, ita_ind=0, pesticide_exam_ind=1 + ), + ) + + with app.app_context(): + from app.models.bookings import Exam + from qsystem import db + + exam_model = Exam.query.filter_by(exam_id=exam["exam_id"]).first() + exam_model.is_pesticide = 1 + exam_model.exam_received_date = None + db.session.add(exam_model) + db.session.commit() + + response = internal_ga_client.get("/csrs/me/") + + assert_json_response(response, 200) + assert json_of(response)["attention_needed"] is True + + +@pytest.mark.parametrize( + ("student_count", "invigilator_count"), + [ + pytest.param(10, 0, id="small-group-requires-at-least-one-invigilator"), + pytest.param(25, 1, id="large-group-requires-two-invigilators"), + ], +) +def test_csr_me_flags_attention_for_group_exam_invigilator_thresholds( + app, internal_ga_client, seeded_data, student_count, invigilator_count +): + """Assert that the csr dashboard flags understaffed group-exam bookings.""" + booking = create_booking( + internal_ga_client, + seeded_data, + days_from_now=5, + invigilator_ids=seeded_data["invigilator_ids"][:invigilator_count], + ) + create_exam( + internal_ga_client, + seeded_data, + booking["booking_id"], + event_id=f"group-{student_count}-{uuid4().hex[:8]}", + exam_type_id=_exam_type_id(app, group_exam_ind=1, ita_ind=1), + number_of_students=student_count, + ) + + response = internal_ga_client.get("/csrs/me/") + + assert_json_response(response, 200) + assert json_of(response)["attention_needed"] is True diff --git a/api/app/tests/flows/test_csr_flows.py b/api/app/tests/flows/test_csr_flows.py new file mode 100644 index 000000000..66d9bc733 --- /dev/null +++ b/api/app/tests/flows/test_csr_flows.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_service_ready_citizen, + json_of, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _csr_state_id(app, state_name: str) -> int: + with app.app_context(): + from app.models.theq import CSRState + + state = CSRState.query.filter_by(csr_state_name=state_name).first() + assert state is not None + return state.csr_state_id + + +def _persisted_csr_state(app, csr_id: int) -> str: + with app.app_context(): + from app.models.theq import CSR + + csr = CSR.query.filter_by(csr_id=csr_id).first() + assert csr is not None + return csr.csr_state.csr_state_name + + +def test_csr_can_transition_from_logout_to_break(internal_ga_client, seeded_data, app): + """Assert that a CSR can update their own state from Logout to Break.""" + break_state_id = _csr_state_id(app, "Break") + + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={"csr_state_id": break_state_id}, + ) + + assert_json_response(response, 200) + assert json_of(response)["csr"]["csr_state"]["csr_state_name"] == "Break" + assert _persisted_csr_state(app, seeded_data["csr_ids"]["ga"]) == "Break" + + +def test_csr_can_transition_from_break_to_back_office( + internal_ga_client, seeded_data, app +): + """Assert that a CSR already on Break can transition themselves into Back Office work.""" + break_state_id = _csr_state_id(app, "Break") + back_office_state_id = _csr_state_id(app, "Back Office") + assert_json_response( + internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={"csr_state_id": break_state_id}, + ), + 200, + ) + + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={"csr_state_id": back_office_state_id}, + ) + + assert_json_response(response, 200) + assert json_of(response)["csr"]["csr_state"]["csr_state_name"] == "Back Office" + assert _persisted_csr_state(app, seeded_data["csr_ids"]["ga"]) == "Back Office" + + +def test_csr_state_update_is_rejected_while_the_csr_has_an_open_ticket( + internal_ga_client, seeded_data, app +): + """Assert that CSR state edits are blocked while the CSR still owns an invited or active ticket.""" + break_state_id = _csr_state_id(app, "Break") + citizen, _service_request = create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Open Ticket Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="quick_trans", + ) + assert_json_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/"), + 200, + ) + + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={"csr_state_id": break_state_id}, + ) + + assert response.status_code == 403, response.get_data(as_text=True) + assert ( + json_of(response)["message"] == "CSR has an open ticket and cannot be edited." + ) + assert _persisted_csr_state(app, seeded_data["csr_ids"]["ga"]) == "Logout" diff --git a/api/app/tests/flows/test_draft_appointment_flows.py b/api/app/tests/flows/test_draft_appointment_flows.py new file mode 100644 index 000000000..5883b0f33 --- /dev/null +++ b/api/app/tests/flows/test_draft_appointment_flows.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +from datetime import datetime, timedelta, timezone + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_draft_appointment, + create_internal_appointment, + create_public_draft_and_payload, + create_public_user, + json_of, + public_slot_payload, + slot_window_to_iso, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _appointment_exists(app, appointment_id: int) -> bool: + with app.app_context(): + from app.models.bookings import Appointment + + return ( + Appointment.query.filter_by(appointment_id=appointment_id).first() + is not None + ) + + +def _backdate_appointment(app, appointment_id: int, *, minutes_ago: int) -> None: + with app.app_context(): + from app.models.bookings import Appointment + from qsystem import db + + appointment = Appointment.query.filter_by(appointment_id=appointment_id).first() + appointment.created_at = datetime.now(timezone.utc) - timedelta( + minutes=minutes_ago + ) + db.session.add(appointment) + db.session.commit() + + +def test_draft_create_persists_is_draft_and_anonymous_fallback_name( + bare_client, seeded_data, app +): + """Assert that anonymous draft creation stores the draft flag and the fallback citizen name.""" + payload, _day_key, _slots = public_slot_payload( + bare_client, seeded_data, minimum_slots=1 + ) + + response = bare_client.post("/appointments/draft", json=payload) + + assert_json_response(response, 201) + appointment = json_of(response)["appointment"] + assert appointment["is_draft"] is True + assert appointment["citizen_name"] == "Draft" + + with app.app_context(): + from app.models.bookings import Appointment + + persisted = Appointment.query.filter_by( + appointment_id=appointment["appointment_id"] + ).first() + assert persisted is not None + assert persisted.is_draft is True + assert persisted.citizen_name == "Draft" + + +def test_draft_delete_removes_the_draft_row(bare_client, seeded_data, app): + """Assert that deleting a draft appointment removes only the draft record from persistence.""" + draft = create_draft_appointment(bare_client, seeded_data) + + response = bare_client.delete(f"/appointments/draft/{draft['appointment_id']}/") + + assert response.status_code == 204, response.get_data(as_text=True) + assert _appointment_exists(app, draft["appointment_id"]) is False + + +def test_confirmed_appointment_creation_consumes_the_previous_draft( + public_client, seeded_data, app +): + """Assert that creating a confirmed appointment with an appointment_draft_id deletes the draft row.""" + create_public_user(public_client) + draft, payload = create_public_draft_and_payload(public_client, seeded_data) + + response = public_client.post("/appointments/", json=payload) + + assert_json_response(response, 201) + appointment = json_of(response)["appointment"] + assert appointment["is_draft"] is False + assert _appointment_exists(app, draft["appointment_id"]) is False + assert _appointment_exists(app, appointment["appointment_id"]) is True + + +def test_draft_flush_deletes_only_expired_drafts( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that draft flushing removes only expired drafts while preserving fresh drafts and confirmed appointments.""" + expired_draft = create_draft_appointment(bare_client, seeded_data) + payload, day_key, slots = public_slot_payload( + bare_client, seeded_data, minimum_slots=2 + ) + fresh_start_time, fresh_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + fresh_response = bare_client.post( + "/appointments/draft", + json={ + **payload, + "start_time": fresh_start_time, + "end_time": fresh_end_time, + }, + ) + assert_json_response(fresh_response, 201) + fresh_draft = json_of(fresh_response)["appointment"] + confirmed_appointment = create_internal_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + _backdate_appointment(app, expired_draft["appointment_id"], minutes_ago=20) + + response = bare_client.post("/appointments/draft/flush") + + assert_json_response(response, 200) + deleted_draft_ids = json_of(response)["deleted_draft_ids"] + assert expired_draft["appointment_id"] in deleted_draft_ids + assert fresh_draft["appointment_id"] not in deleted_draft_ids + assert confirmed_appointment["appointment_id"] not in deleted_draft_ids + assert _appointment_exists(app, expired_draft["appointment_id"]) is False + assert _appointment_exists(app, fresh_draft["appointment_id"]) is True + assert _appointment_exists(app, confirmed_appointment["appointment_id"]) is True diff --git a/api/app/tests/flows/test_exam_export_flows.py b/api/app/tests/flows/test_exam_export_flows.py new file mode 100644 index 000000000..84e0917fe --- /dev/null +++ b/api/app/tests/flows/test_exam_export_flows.py @@ -0,0 +1,180 @@ +from __future__ import annotations + +import csv +import io +from datetime import datetime, timedelta, timezone +from uuid import uuid4 +from zoneinfo import ZoneInfo + +import pytest +from app.tests.api_test_support import json_of + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _set_finance_designate(app, *, username: str, enabled: int): + with app.app_context(): + from app.models.theq import CSR + from qsystem import db + + csr = CSR.query.filter_by(username=username).first() + csr.finance_designate = enabled + db.session.add(csr) + db.session.commit() + + +def _create_export_exam( + app, + seeded_data, + *, + office_id: int, + start_time: datetime, + exam_name: str, + booking_name: str, + room_id: int | None = None, + invigilator_ids: list[int] | None = None, +) -> dict[str, int]: + with app.app_context(): + from app.models.bookings import Booking, Exam, Invigilator + from qsystem import db + + booking = Booking( + office_id=office_id, + room_id=room_id, + start_time=start_time, + end_time=start_time + timedelta(hours=2), + fees="false", + booking_name=booking_name, + booking_contact_information="booking@example.com", + ) + if invigilator_ids: + booking.invigilators = ( + Invigilator.query.filter(Invigilator.invigilator_id.in_(invigilator_ids)) + .order_by(Invigilator.invigilator_id) + .all() + ) + db.session.add(booking) + db.session.flush() + + exam = Exam( + booking_id=booking.booking_id, + exam_type_id=seeded_data["exam_type_id"], + office_id=office_id, + event_id=f"event-{uuid4().hex[:8]}", + exam_name=exam_name, + examinee_name="Codex Examinee", + expiry_date=start_time + timedelta(days=30), + notes="Export flow", + number_of_students=1, + exam_method="paper", + exam_written_ind=0, + offsite_location="Test Office", + ) + db.session.add(exam) + db.session.commit() + return {"booking_id": booking.booking_id, "exam_id": exam.exam_id} + + +def _export_rows(response) -> list[list[str]]: + return list(csv.reader(io.StringIO(response.get_data(as_text=True)))) + + +def _row_for_exam(rows: list[list[str]], exam_name: str) -> list[str]: + return next(row for row in rows[1:] if row[3] == exam_name) + + +def test_exam_export_rejects_missing_or_invalid_dates(internal_ga_client): + """Assert that export validation fails loudly for missing and malformed date parameters.""" + missing_dates = internal_ga_client.get("/exams/export/?end_date=2024-07-15") + invalid_dates = internal_ga_client.get( + "/exams/export/?start_date=2024-13-40&end_date=2024-07-15" + ) + + assert missing_dates.status_code == 422, missing_dates.get_data(as_text=True) + assert json_of(missing_dates)["message"] == "Must provide both start and end time" + + assert invalid_dates.status_code == 422, invalid_dates.get_data(as_text=True) + assert json_of(invalid_dates)["message"] == "Unable to return date time string" + + +def test_exam_export_localizes_times_and_leaves_blank_room_and_invigilator_columns( + internal_ga_client, seeded_data, app +): + """Assert that CSV rows keep localized timestamps while tolerating bookings without room or invigilator data.""" + start_time = datetime(2024, 7, 15, 17, 0, tzinfo=timezone.utc) + _create_export_exam( + app, + seeded_data, + office_id=seeded_data["office_ids"]["test_office"], + start_time=start_time, + exam_name="No Room Exam", + booking_name="No Room Booking", + room_id=None, + invigilator_ids=[], + ) + + export_date = ( + start_time.astimezone( + ZoneInfo(seeded_data["office_timezones"]["test_office"]) + ) + .date() + .isoformat() + ) + response = internal_ga_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}" + ) + + assert response.status_code == 200, response.get_data(as_text=True) + row = _row_for_exam(_export_rows(response), "No Room Exam") + + expected_start = start_time.astimezone( + ZoneInfo(seeded_data["office_timezones"]["test_office"]) + ).strftime("%Y-%m-%d %I:%M %p") + expected_end = (start_time + timedelta(hours=2)).astimezone( + ZoneInfo(seeded_data["office_timezones"]["test_office"]) + ).strftime("%Y-%m-%d %I:%M %p") + + assert row[6] == "" + assert row[7] == "" + assert row[10] == f'="{expected_start}"' + assert row[11] == f'="{expected_end}"' + + +def test_exam_export_limits_non_designates_to_their_own_office_but_allows_designates_to_export_all_offices( + internal_ga_client, internal_nonqtxn_client, seeded_data, app +): + """Assert that finance designates can export rows from other offices while non-designates stay office-scoped.""" + start_time = datetime(2024, 7, 20, 17, 0, tzinfo=timezone.utc) + _create_export_exam( + app, + seeded_data, + office_id=seeded_data["office_ids"]["victoria"], + start_time=start_time, + exam_name="Victoria Export Exam", + booking_name="Victoria Export Booking", + ) + export_date = ( + start_time.astimezone(ZoneInfo(seeded_data["office_timezones"]["victoria"])) + .date() + .isoformat() + ) + + _set_finance_designate(app, username="cfms-postman-operator", enabled=0) + non_designate_response = internal_ga_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}" + ) + + assert non_designate_response.status_code == 200, non_designate_response.get_data( + as_text=True + ) + non_designate_exam_names = {row[3] for row in _export_rows(non_designate_response)[1:]} + assert "Victoria Export Exam" not in non_designate_exam_names + + _set_finance_designate(app, username="cfms-postman-non-operator", enabled=1) + designate_response = internal_nonqtxn_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}" + ) + + assert designate_response.status_code == 200, designate_response.get_data(as_text=True) + designate_exam_names = {row[3] for row in _export_rows(designate_response)[1:]} + assert "Victoria Export Exam" in designate_exam_names diff --git a/api/app/tests/flows/test_exam_integration_flows.py b/api/app/tests/flows/test_exam_integration_flows.py new file mode 100644 index 000000000..7402959c0 --- /dev/null +++ b/api/app/tests/flows/test_exam_integration_flows.py @@ -0,0 +1,111 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.auth.auth_support import ( + build_bcmp_exam_payload, + create_internal_exam_bundle, + patch_exam_integrations, +) +from app.tests.helpers.exams import ( + exam_invigilator_id, + exam_upload_received_ind, + seed_exam_bcmp_job, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def test_bcmp_exam_create_returns_the_bcmp_job_id( + internal_ga_client, monkeypatch, seeded_data +): + """Assert that BCMP exam creation returns the created job id.""" + patch_exam_integrations( + monkeypatch, create_individual_response={"jobId": "bcmp-job-999"} + ) + + response = internal_ga_client.post( + "/exams/bcmp/", + json=build_bcmp_exam_payload(seeded_data), + ) + + assert_json_response(response, 201) + assert json_of(response)["bcmp_job_id"] == "bcmp-job-999" + + +def test_bcmp_status_marks_uploaded_exams_as_received( + app, internal_ga_client, monkeypatch, seeded_data +): + """Assert that BCMP bulk status updates persist upload_received_ind for matching exams.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + seed_exam_bcmp_job( + app, + exam["exam_id"], + bcmp_job_id="bcmp-job-555", + upload_received_ind=0, + ) + patch_exam_integrations( + monkeypatch, + bulk_jobs=[{"jobId": "bcmp-job-555", "jobStatus": "RESPONSE_UPLOADED"}], + ) + + response = internal_ga_client.post("/exams/bcmp_status/", json={}) + + assert_json_response(response, 200) + assert json_of(response)["exams_updated"] == [exam["exam_id"]] + assert exam_upload_received_ind(app, exam["exam_id"]) == 1 + + +def test_exam_transfer_returns_the_bcmp_transfer_response( + internal_ga_client, monkeypatch, seeded_data +): + """Assert that exam transfer returns the accepted BCMP response payload.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + patch_exam_integrations( + monkeypatch, transfer_response={"jobId": "transfer-job-999"} + ) + + response = internal_ga_client.post(f"/exams/{exam['exam_id']}/transfer/") + + assert_json_response(response, 202) + assert json_of(response)["bcmp"]["jobId"] == "transfer-job-999" + + +def test_exam_download_returns_the_generated_pdf( + internal_ga_client, monkeypatch, seeded_data +): + """Assert that exam downloads stream the generated PDF bytes when the package is ready.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + patch_exam_integrations( + monkeypatch, + download_job={ + "jobStatus": "PACKAGE_GENERATED", + "jobProperties": {"EXAM_PACKAGE_URL": "https://example.com/package.pdf"}, + }, + download_bytes=b"exam-pdf", + ) + + response = internal_ga_client.get(f"/exams/{exam['exam_id']}/download/") + + assert response.status_code == 200, response.get_data(as_text=True) + assert response.mimetype == "application/pdf" + assert response.get_data() == b"exam-pdf" + + +def test_email_invigilator_updates_the_exam_record( + app, internal_ga_client, monkeypatch, seeded_data +): + """Assert that emailing the invigilator persists the selected invigilator on the exam.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + patch_exam_integrations(monkeypatch, email_result=True) + + response = internal_ga_client.post( + f"/exams/{exam['exam_id']}/email_invigilator/", + json={ + "invigilator_id": seeded_data["invigilator_ids"][0], + "invigilator_name": "Homer Simpson", + "invigilator_email": "homer@example.com", + "invigilator_phone": "2505550100", + }, + ) + + assert_json_response(response, 200) + assert exam_invigilator_id(app, exam["exam_id"]) == seeded_data["invigilator_ids"][0] diff --git a/api/app/tests/flows/test_exam_variant_flows.py b/api/app/tests/flows/test_exam_variant_flows.py new file mode 100644 index 000000000..040aea6c9 --- /dev/null +++ b/api/app/tests/flows/test_exam_variant_flows.py @@ -0,0 +1,314 @@ +from __future__ import annotations + +import csv +import io +from datetime import datetime +from uuid import uuid4 +from zoneinfo import ZoneInfo + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_booking, + create_exam, + json_of, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _exam_type_id( + app, + *, + exam_type_name: str | None = None, + group_exam_ind: int | None = None, + ita_ind: int | None = None, + pesticide_exam_ind: int | None = None, +) -> int: + with app.app_context(): + from app.models.bookings import ExamType + + query = ExamType.query + if exam_type_name is not None: + query = query.filter_by(exam_type_name=exam_type_name) + if group_exam_ind is not None: + query = query.filter_by(group_exam_ind=group_exam_ind) + if ita_ind is not None: + query = query.filter_by(ita_ind=ita_ind) + if pesticide_exam_ind is not None: + query = query.filter_by(pesticide_exam_ind=pesticide_exam_ind) + + exam_type = query.order_by(ExamType.exam_type_id).first() + assert exam_type is not None + return exam_type.exam_type_id + + +def _pesticide_office_id(app) -> int: + with app.app_context(): + from app.models.theq import Office + + office = Office.query.filter_by(office_name="Pesticide Offsite").first() + assert office is not None + return office.office_id + + +def _export_date(booking: dict, timezone_name: str) -> str: + booking_start = datetime.fromisoformat(booking["start_time"].replace("Z", "+00:00")) + return booking_start.astimezone(ZoneInfo(timezone_name)).date().isoformat() + + +def test_pesticide_individual_exam_preserves_type_and_sets_job_event_id( + app, monkeypatch, internal_ga_client, seeded_data +): + """Assert that pesticide individual exam creation keeps the chosen type and maps BCMP job ids to event ids.""" + from app.resources.bookings.exam.exam_post import ExamPost + + exam_type_id = _exam_type_id( + app, group_exam_ind=0, ita_ind=0, pesticide_exam_ind=1 + ) + monkeypatch.setattr( + ExamPost.bcmp_service, + "check_exam_status", + lambda exam: {"jobProperties": {"JOB_ID": f"job-{exam.exam_name}"}}, + ) + + response = internal_ga_client.post( + "/exams/", + json={ + "event_id": "placeholder", + "exam_method": "paper", + "exam_name": "Pesticide Individual", + "exam_type_id": exam_type_id, + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Pesticide individual flow", + "number_of_students": 1, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now().isoformat(), + "is_pesticide": 1, + "sbc_managed": "sbc", + "ind_or_group": "individual", + "fees": "25.00", + "receipt_number": "R-100", + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + assert body["exam"]["event_id"] == "job-Pesticide Individual" + + with app.app_context(): + from app.models.bookings import Exam + + exam = Exam.query.filter_by(exam_id=body["exam"]["exam_id"]).first() + assert exam.exam_type_id == exam_type_id + assert exam.office_id == seeded_data["office_ids"]["test_office"] + assert exam.receipt == "R-100" + assert exam.sbc_managed_ind == 1 + + +def test_pesticide_non_sbc_exam_is_reassigned_to_the_pesticide_offsite_office( + app, monkeypatch, internal_ga_client, seeded_data +): + """Assert that non-SBC pesticide exams move to the dedicated pesticide office before persistence.""" + from app.resources.bookings.exam.exam_post import ExamPost + + monkeypatch.setattr( + ExamPost.bcmp_service, + "check_exam_status", + lambda exam: {"jobProperties": {"JOB_ID": "job-non-sbc"}}, + ) + + response = internal_ga_client.post( + "/exams/", + json={ + "event_id": "placeholder", + "exam_method": "paper", + "exam_name": "Pesticide Non SBC", + "exam_type_id": _exam_type_id( + app, group_exam_ind=0, ita_ind=0, pesticide_exam_ind=1 + ), + "exam_written_ind": 0, + "examinee_name": "Codex Examinee", + "notes": "Pesticide non-SBC flow", + "number_of_students": 1, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now().isoformat(), + "is_pesticide": 1, + "sbc_managed": "non-sbc", + "ind_or_group": "individual", + "fees": "25.00", + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + + with app.app_context(): + from app.models.bookings import Exam + + exam = Exam.query.filter_by(exam_id=body["exam"]["exam_id"]).first() + assert exam.office_id == _pesticide_office_id(app) + assert exam.event_id == "job-non-sbc" + + +def test_pesticide_group_exam_normalizes_candidates_and_uses_group_environment_type( + app, monkeypatch, internal_ga_client, seeded_data +): + """Assert that pesticide group exam creation normalizes candidate payloads for persistence.""" + from app.resources.bookings.exam.exam_post import ExamPost + + monkeypatch.setattr( + ExamPost.bcmp_service, + "check_exam_status", + lambda exam: {"jobProperties": {"JOB_ID": "job-group"}}, + ) + candidate_exam_type_id = _exam_type_id( + app, group_exam_ind=0, ita_ind=0, pesticide_exam_ind=1 + ) + + response = internal_ga_client.post( + "/exams/", + json={ + "event_id": "placeholder", + "exam_method": "paper", + "exam_name": "Pesticide Group", + "exam_type_id": _exam_type_id( + app, exam_type_name="Group Environment Exam" + ), + "exam_written_ind": 0, + "examinee_name": "Codex Group", + "notes": "Pesticide group flow", + "number_of_students": 2, + "office_id": seeded_data["office_ids"]["test_office"], + "offsite_location": "Test Office", + "expiry_date": datetime.now().isoformat(), + "is_pesticide": 1, + "sbc_managed": "sbc", + "ind_or_group": "group", + "fees": "25.00", + "candidates": [ + { + "name": "Candidate One", + "email": "candidate-one@example.com", + "exam_type_id": candidate_exam_type_id, + "fees": "25.00", + "billTo": "candidate", + "receipt": "R-201", + "payeeName": "Candidate One", + "payeeEmail": "candidate-one@example.com", + }, + { + "name": "Candidate Two", + "email": "candidate-two@example.com", + "exam_type_id": candidate_exam_type_id, + "fees": "30.00", + "billTo": "employer", + "receipt": "R-202", + "payeeName": "Employer Two", + "payeeEmail": "employer-two@example.com", + }, + ], + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + + with app.app_context(): + from app.models.bookings import Exam + + exam = Exam.query.filter_by(exam_id=body["exam"]["exam_id"]).first() + assert exam.exam_type.exam_type_name == "Group Environment Exam" + assert exam.event_id == "job-group" + assert exam.candidates_list == [ + { + "examinee_name": "Candidate One", + "examinee_email": "candidate-one@example.com", + "exam_type_id": candidate_exam_type_id, + "fees": "25.00", + "payee_ind": 1, + "receipt": "R-201", + "receipt_number": "R-201", + "payee_name": "Candidate One", + "payee_email": "candidate-one@example.com", + }, + { + "examinee_name": "Candidate Two", + "examinee_email": "candidate-two@example.com", + "exam_type_id": candidate_exam_type_id, + "fees": "30.00", + "payee_ind": 0, + "receipt": "R-202", + "receipt_number": "R-202", + "payee_name": "Employer Two", + "payee_email": "employer-two@example.com", + }, + ] + + +def test_exam_export_all_bookings_includes_non_exam_booking_rows( + internal_ga_client, seeded_data +): + """Assert that the all-bookings export includes rows for bookings without attached exams.""" + booking = create_booking( + internal_ga_client, + seeded_data, + days_from_now=5, + booking_name="Booking Without Exam", + ) + export_date = _export_date( + booking, seeded_data["office_timezones"]["test_office"] + ) + + response = internal_ga_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}&exam_type=all_bookings" + ) + + assert response.status_code == 200, response.get_data(as_text=True) + export_body = response.get_data(as_text=True) + assert "Non Exam Booking" in export_body + assert "Booking Without Exam" in export_body + + +def test_exam_export_all_non_ita_excludes_ita_exam_rows( + app, internal_ga_client, seeded_data +): + """Assert that the non-ITA export omits ITA exam rows while preserving non-ITA rows.""" + ita_booking = create_booking(internal_ga_client, seeded_data, days_from_now=5) + non_ita_booking = create_booking(internal_ga_client, seeded_data, days_from_now=5) + + create_exam( + internal_ga_client, + seeded_data, + ita_booking["booking_id"], + event_id=f"ita-{uuid4().hex[:8]}", + exam_type_id=_exam_type_id(app, group_exam_ind=0, ita_ind=1, pesticide_exam_ind=0), + exam_name="ITA Exam", + ) + create_exam( + internal_ga_client, + seeded_data, + non_ita_booking["booking_id"], + event_id=f"nonita-{uuid4().hex[:8]}", + exam_type_id=_exam_type_id( + app, group_exam_ind=0, ita_ind=0, pesticide_exam_ind=0 + ), + exam_name="Non ITA Exam", + ) + + export_date = _export_date( + non_ita_booking, seeded_data["office_timezones"]["test_office"] + ) + response = internal_ga_client.get( + f"/exams/export/?start_date={export_date}&end_date={export_date}&exam_type=all_non_ita" + ) + + assert response.status_code == 200, response.get_data(as_text=True) + export_body = response.get_data(as_text=True) + rows = list(csv.reader(io.StringIO(export_body))) + exam_names = {row[3] for row in rows[1:]} + assert "Non ITA Exam" in exam_names + assert "ITA Exam" not in exam_names diff --git a/api/app/tests/flows/test_queue_flows.py b/api/app/tests/flows/test_queue_flows.py new file mode 100644 index 000000000..fd97618a5 --- /dev/null +++ b/api/app/tests/flows/test_queue_flows.py @@ -0,0 +1,681 @@ +from __future__ import annotations + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + json_of, +) +from app.tests.api_test_support import ( + create_citizen as _create_citizen, +) +from app.tests.api_test_support import ( + create_queue_ready_citizen as _create_queue_ready_citizen, +) +from app.tests.api_test_support import ( + create_service_ready_citizen as _create_service_ready_citizen, +) +from app.tests.api_test_support import ( + create_service_request as _create_service_request, +) +from app.tests.contracts.conftest import validate_schema +from app.tests.contracts.schemas import CITIZEN_RESPONSE_SCHEMA + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _citizen_from_response(response, expected_status: int = 200): + assert_json_response(response, expected_status) + body = json_of(response) + validate_schema(body, CITIZEN_RESPONSE_SCHEMA) + return body["citizen"] + + +def _primary_service_request(citizen: dict) -> dict: + return citizen["service_reqs"][0] + + +def _period_names(citizen: dict) -> list[str]: + return [ + period["ps"]["ps_name"] + for period in _primary_service_request(citizen)["periods"] + ] + + +def _latest_period_name(record: dict) -> str: + return record["periods"][-1]["ps"]["ps_name"] + + +def _assert_period_count_delta(record: dict, previous_count: int, *, delta: int = 1): + assert len(record["periods"]) == previous_count + delta + + +def _queue_ids(api_client) -> list[int]: + response = api_client.get("/citizens/") + assert_json_response(response, 200) + return [citizen["citizen_id"] for citizen in json_of(response)["citizens"]] + + +def test_qt1_specific_invite_appends_an_invited_period(internal_ga_client, seeded_data): + """Assert that QT1 specific invites append an Invited period to the citizen's active request.""" + citizen, _service_request, queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT1 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=3, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="Needs property tax", + ) + + queued_period_count = len(_primary_service_request(queued_citizen)["periods"]) + invited_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/invite/") + ) + + assert _latest_period_name(_primary_service_request(invited_citizen)) == "Invited" + _assert_period_count_delta( + _primary_service_request(invited_citizen), queued_period_count + ) + + +def test_qt1_begin_service_after_invite_appends_a_being_served_period( + internal_ga_client, seeded_data +): + """Assert that QT1 begin-service transitions append a Being Served period after a specific invite.""" + citizen, _service_request, _queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT1 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=3, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="Needs property tax", + ) + + invited_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/invite/") + ) + invited_period_count = len(_primary_service_request(invited_citizen)["periods"]) + serving_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + + assert ( + _latest_period_name(_primary_service_request(serving_citizen)) == "Being Served" + ) + _assert_period_count_delta( + _primary_service_request(serving_citizen), invited_period_count + ) + + +def test_qt1_reactivate_service_request_restores_a_being_served_period( + internal_ga_client, seeded_data +): + """Assert that QT1 reactivation appends a fresh Being Served period to the original request.""" + citizen, first_service, _queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT1 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=3, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="Needs property tax", + ) + + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/invite/") + ) + serving_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + serving_period_count = len(_primary_service_request(serving_citizen)["periods"]) + + second_service = _create_service_request( + internal_ga_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + reactivate_response = internal_ga_client.post( + f"/service_requests/{first_service['sr_id']}/activate/" + ) + + assert_json_response(reactivate_response, 200) + reactivated_request = json_of(reactivate_response)["service_request"] + + assert second_service["service_id"] == seeded_data["service_ids"]["msp"] + assert reactivated_request["sr_id"] == first_service["sr_id"] + assert _latest_period_name(reactivated_request) == "Being Served" + _assert_period_count_delta(reactivated_request, serving_period_count) + + +def test_qt1_finish_service_completes_both_requests_and_clears_the_queue( + internal_ga_client, seeded_data, app +): + """Assert that QT1 finish-service completes both requests, updates citizen state, and clears the queue.""" + citizen, first_service, _queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT1 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=3, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="Needs property tax", + ) + + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/invite/") + ) + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + _create_service_request( + internal_ga_client, + citizen["citizen_id"], + service_id=seeded_data["service_ids"]["msp"], + channel_id=seeded_data["channel_ids"]["email"], + quantity=1, + ) + assert_json_response( + internal_ga_client.post( + f"/service_requests/{first_service['sr_id']}/activate/" + ), + 200, + ) + + finished_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") + ) + + assert [ + service_request["sr_state"]["sr_code"] + for service_request in finished_citizen["service_reqs"] + ] == [ + "Complete", + "Complete", + ] + assert finished_citizen["cs"]["cs_state_name"] == "Received Services" + assert _queue_ids(internal_ga_client) == [] + + with app.app_context(): + from app.models.theq import Citizen, ServiceReq + + citizen_model = Citizen.query.filter_by( + citizen_id=citizen["citizen_id"] + ).first() + sr_models = ( + ServiceReq.query.filter_by(citizen_id=citizen["citizen_id"]) + .order_by(ServiceReq.sr_number) + .all() + ) + assert citizen_model.cs.cs_state_name == "Received Services" + assert [service_request.sr_state.sr_code for service_request in sr_models] == [ + "Complete", + "Complete", + ] + + +def test_qt2_begin_service_appends_a_being_served_period( + internal_ga_client, seeded_data +): + """Assert that QT2 begin-service appends a Being Served period to the active request.""" + citizen, service_request = _create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT2 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=2, + counter_id_key="counter", + ) + + initial_period_count = len(service_request["periods"]) + serving_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + + assert ( + _latest_period_name(_primary_service_request(serving_citizen)) == "Being Served" + ) + _assert_period_count_delta( + _primary_service_request(serving_citizen), initial_period_count + ) + + +def test_qt2_place_on_hold_appends_an_on_hold_period(internal_ga_client, seeded_data): + """Assert that QT2 place-on-hold appends an On hold period to the active request.""" + citizen, _service_request = _create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT2 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=2, + counter_id_key="counter", + ) + + serving_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + serving_period_count = len(_primary_service_request(serving_citizen)["periods"]) + held_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/place_on_hold/") + ) + + assert _latest_period_name(_primary_service_request(held_citizen)) == "On hold" + _assert_period_count_delta( + _primary_service_request(held_citizen), serving_period_count + ) + + +def test_qt2_resume_service_appends_being_served_after_hold( + internal_ga_client, seeded_data +): + """Assert that QT2 resume transitions append Being Served after the existing hold history.""" + citizen, _service_request = _create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT2 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=2, + counter_id_key="counter", + ) + + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + held_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/place_on_hold/") + ) + held_period_count = len(_primary_service_request(held_citizen)["periods"]) + resumed_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + + assert ( + _latest_period_name(_primary_service_request(resumed_citizen)) == "Being Served" + ) + _assert_period_count_delta( + _primary_service_request(resumed_citizen), held_period_count + ) + assert _period_names(resumed_citizen)[-3:] == [ + "Being Served", + "On hold", + "Being Served", + ] + + +def test_qt2_finish_service_marks_the_request_complete_and_clears_the_queue( + internal_ga_client, seeded_data +): + """Assert that QT2 finish-service leaves the request Complete and removes the citizen from the queue.""" + citizen, _service_request = _create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT2 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=2, + counter_id_key="counter", + ) + + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/place_on_hold/") + ) + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + finished_citizen = _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") + ) + + assert ( + _primary_service_request(finished_citizen)["sr_state"]["sr_code"] == "Complete" + ) + assert _queue_ids(internal_ga_client) == [] + + +def test_qt3_citizen_leaves_after_create(internal_ga_client, app): + """Assert that QT3 marks a newly created citizen as having left before service.""" + citizen = _create_citizen(internal_ga_client, 0, name="QT3 Citizen") + leave_response = internal_ga_client.post( + f"/citizens/{citizen['citizen_id']}/citizen_left/" + ) + + assert_json_response(leave_response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + citizen_model = Citizen.query.filter_by( + citizen_id=citizen["citizen_id"] + ).first() + assert citizen_model.cs.cs_state_name == "Left before receiving services" + + +def test_qt4_citizen_leaves_after_waiting(internal_ga_client, seeded_data, app): + """Assert that QT4 preserves the left-state transition after the citizen joins the queue.""" + citizen, _service_request, _queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT4 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + leave_response = internal_ga_client.post( + f"/citizens/{citizen['citizen_id']}/citizen_left/" + ) + + assert_json_response(leave_response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + citizen_model = Citizen.query.filter_by( + citizen_id=citizen["citizen_id"] + ).first() + assert citizen_model.cs.cs_state_name == "Left before receiving services" + + +def test_qt5_update_service_request_quantity_and_service( + internal_ga_client, seeded_data, app +): + """Assert that QT5 preserves service-request updates while a citizen is being served.""" + citizen, service_request = _create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT5 Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=3, + ) + _citizen_from_response( + internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") + ) + + quantity_update = internal_ga_client.put( + f"/service_requests/{service_request['sr_id']}/", json={"quantity": 5} + ) + service_update = internal_ga_client.put( + f"/service_requests/{service_request['sr_id']}/", + json={"service_id": seeded_data["service_ids"]["msp"]}, + ) + finish_response = internal_ga_client.post( + f"/citizens/{citizen['citizen_id']}/finish_service/" + ) + + assert_json_response(quantity_update, 200) + assert json_of(quantity_update)["service_request"]["quantity"] == 5 + assert_json_response(service_update, 200) + assert ( + json_of(service_update)["service_request"]["service_id"] + == seeded_data["service_ids"]["msp"] + ) + assert_json_response(finish_response, 200) + + with app.app_context(): + from app.models.theq import ServiceReq + + service_request_model = ServiceReq.query.filter_by( + sr_id=service_request["sr_id"] + ).first() + assert service_request_model.quantity == 5 + assert service_request_model.service_id == seeded_data["service_ids"]["msp"] + + +def test_qt6_first_generic_invite_prefers_the_quick_transaction_counter( + internal_ga_client, seeded_data +): + """Assert that QT6 generic invite selects the quick-transaction citizen before the standard queue.""" + _first_citizen, _first_service_request, _first_queued_citizen = ( + _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT6 First", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + ) + second_citizen, _second_service_request, second_queued_citizen = ( + _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=1, + name="QT6 Second", + service_id_key="msp", + channel_id_key="email", + quantity=1, + counter_id_key="quick_trans", + qt_xn_citizen_ind=1, + ) + ) + + queued_period_count = len( + _primary_service_request(second_queued_citizen)["periods"] + ) + invited_citizen = _citizen_from_response( + internal_ga_client.post("/citizens/invite/", json={}) + ) + + assert invited_citizen["citizen_id"] == second_citizen["citizen_id"] + assert invited_citizen["qt_xn_citizen_ind"] == 1 + assert _latest_period_name(_primary_service_request(invited_citizen)) == "Invited" + _assert_period_count_delta( + _primary_service_request(invited_citizen), queued_period_count + ) + + +def test_qt6_second_generic_invite_returns_the_remaining_standard_queue_citizen( + internal_ga_client, seeded_data +): + """Assert that QT6 returns the remaining standard-queue citizen after the quick-transaction citizen finishes.""" + first_citizen, _first_service_request, first_queued_citizen = ( + _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="QT6 First", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + ) + second_citizen, _second_service_request, _second_queued_citizen = ( + _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=1, + name="QT6 Second", + service_id_key="msp", + channel_id_key="email", + quantity=1, + counter_id_key="quick_trans", + qt_xn_citizen_ind=1, + ) + ) + + first_invited_citizen = _citizen_from_response( + internal_ga_client.post("/citizens/invite/", json={}) + ) + _citizen_from_response( + internal_ga_client.post( + f"/citizens/{second_citizen['citizen_id']}/begin_service/" + ) + ) + _citizen_from_response( + internal_ga_client.post( + f"/citizens/{second_citizen['citizen_id']}/finish_service/" + ) + ) + + queued_period_count = len(_primary_service_request(first_queued_citizen)["periods"]) + second_invited_citizen = _citizen_from_response( + internal_ga_client.post("/citizens/invite/", json={}) + ) + + assert first_invited_citizen["citizen_id"] == second_citizen["citizen_id"] + assert second_invited_citizen["citizen_id"] == first_citizen["citizen_id"] + assert second_invited_citizen["qt_xn_citizen_ind"] == 0 + assert ( + _latest_period_name(_primary_service_request(second_invited_citizen)) + == "Invited" + ) + _assert_period_count_delta( + _primary_service_request(second_invited_citizen), queued_period_count + ) + + assert_json_response( + internal_ga_client.post( + f"/citizens/{first_citizen['citizen_id']}/citizen_left/" + ), + 200, + ) + assert _queue_ids(internal_ga_client) == [] + + +def test_qt7_first_generic_invite_prefers_the_standard_counter( + internal_nonqtxn_client, seeded_data +): + """Assert that QT7 generic invite selects the standard-queue citizen for a non-quick-transaction CSR.""" + first_citizen, _first_service_request, first_queued_citizen = ( + _create_queue_ready_citizen( + internal_nonqtxn_client, + seeded_data, + position=0, + name="QT7 First", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + ) + _second_citizen, _second_service_request, _second_queued_citizen = ( + _create_queue_ready_citizen( + internal_nonqtxn_client, + seeded_data, + position=1, + name="QT7 Second", + service_id_key="msp", + channel_id_key="email", + quantity=1, + counter_id_key="quick_trans", + qt_xn_citizen_ind=1, + ) + ) + + queued_period_count = len(_primary_service_request(first_queued_citizen)["periods"]) + invited_citizen = _citizen_from_response( + internal_nonqtxn_client.post("/citizens/invite/", json={}) + ) + + assert invited_citizen["citizen_id"] == first_citizen["citizen_id"] + assert invited_citizen["qt_xn_citizen_ind"] == 0 + assert _latest_period_name(_primary_service_request(invited_citizen)) == "Invited" + _assert_period_count_delta( + _primary_service_request(invited_citizen), queued_period_count + ) + + +def test_qt7_second_generic_invite_returns_the_remaining_quick_transaction_citizen( + internal_nonqtxn_client, seeded_data +): + """Assert that QT7 returns the remaining quick-transaction citizen after the standard citizen finishes.""" + first_citizen, _first_service_request, _first_queued_citizen = ( + _create_queue_ready_citizen( + internal_nonqtxn_client, + seeded_data, + position=0, + name="QT7 First", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + ) + second_citizen, _second_service_request, second_queued_citizen = ( + _create_queue_ready_citizen( + internal_nonqtxn_client, + seeded_data, + position=1, + name="QT7 Second", + service_id_key="msp", + channel_id_key="email", + quantity=1, + counter_id_key="quick_trans", + qt_xn_citizen_ind=1, + ) + ) + + first_invited_citizen = _citizen_from_response( + internal_nonqtxn_client.post("/citizens/invite/", json={}) + ) + _citizen_from_response( + internal_nonqtxn_client.post( + f"/citizens/{first_citizen['citizen_id']}/begin_service/" + ) + ) + _citizen_from_response( + internal_nonqtxn_client.post( + f"/citizens/{first_citizen['citizen_id']}/finish_service/" + ) + ) + + queued_period_count = len( + _primary_service_request(second_queued_citizen)["periods"] + ) + second_invited_citizen = _citizen_from_response( + internal_nonqtxn_client.post("/citizens/invite/", json={}) + ) + + assert first_invited_citizen["citizen_id"] == first_citizen["citizen_id"] + assert second_invited_citizen["citizen_id"] == second_citizen["citizen_id"] + assert second_invited_citizen["qt_xn_citizen_ind"] == 1 + assert ( + _latest_period_name(_primary_service_request(second_invited_citizen)) + == "Invited" + ) + _assert_period_count_delta( + _primary_service_request(second_invited_citizen), queued_period_count + ) + + assert_json_response( + internal_nonqtxn_client.post( + f"/citizens/{second_citizen['citizen_id']}/citizen_left/" + ), + 200, + ) + assert _queue_ids(internal_nonqtxn_client) == [] diff --git a/api/app/tests/flows/test_reminder_flows.py b/api/app/tests/flows/test_reminder_flows.py new file mode 100644 index 000000000..381b3a3f1 --- /dev/null +++ b/api/app/tests/flows/test_reminder_flows.py @@ -0,0 +1,83 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.auth.auth_support import create_public_appointment +from app.tests.helpers.appointments import ( + align_current_pacific_time_for_appointment, + configure_public_user_reminders, + create_internal_reminder_appointment, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def test_email_reminders_include_opted_in_public_users( + monkeypatch, public_client, reminder_job_client, seeded_data +): + """Assert that opted-in public users appear in the email reminder payload.""" + user = configure_public_user_reminders( + public_client, + email="public@example.com", + send_email_reminders=True, + ) + appointment = create_public_appointment(public_client, seeded_data) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["limited_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/email/") + + assert_json_response(response, 200) + assert any( + item["display_name"] == user["display_name"] + and item["email"] == "public@example.com" + for item in json_of(response)["appointments"] + ) + + +def test_sms_reminders_include_anonymous_phone_appointments( + internal_ga_client, monkeypatch, reminder_job_client, seeded_data +): + """Assert that anonymous appointments with a valid phone number appear in SMS reminders.""" + appointment = create_internal_reminder_appointment( + internal_ga_client, + seeded_data, + contact_information="2505550110", + ) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["test_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/sms/") + + assert_json_response(response, 200) + assert any( + item["display_name"] == appointment["citizen_name"] + and item["user_telephone"] == "2505550110" + for item in json_of(response)["appointments"] + ) + + +def test_email_reminders_exclude_public_users_who_did_not_opt_in( + monkeypatch, public_client, reminder_job_client, seeded_data +): + """Assert that public users without email opt-in are excluded from reminder delivery.""" + configure_public_user_reminders( + public_client, + email="public@example.com", + send_email_reminders=False, + ) + appointment = create_public_appointment(public_client, seeded_data) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["limited_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/email/") + + assert_json_response(response, 200) + assert json_of(response)["appointments"] == [] diff --git a/api/app/tests/flows/test_service_refresh_flows.py b/api/app/tests/flows/test_service_refresh_flows.py new file mode 100644 index 000000000..7c5f01c9b --- /dev/null +++ b/api/app/tests/flows/test_service_refresh_flows.py @@ -0,0 +1,47 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.auth.auth_support import promote_internal_csr_to_support + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def test_ga_can_refresh_services_for_their_own_office( + internal_ga_client, seeded_data +): + """Assert that a GA can refresh the service lists for their own office.""" + response = internal_ga_client.get( + f"/services/refresh/?office_id={seeded_data['office_ids']['test_office']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + assert body["office_id"] == seeded_data["office_ids"]["test_office"] + assert isinstance(body["quick_list"], list) + assert isinstance(body["back_office_list"], list) + + +def test_ga_is_rejected_when_refreshing_a_different_office( + internal_ga_client, seeded_data +): + """Assert that GAs cannot refresh service lists for a different office.""" + response = internal_ga_client.get( + f"/services/refresh/?office_id={seeded_data['office_ids']['limited_office']}" + ) + + assert response.status_code == 403, response.get_data(as_text=True) + assert "cannot refresh" in response.get_data(as_text=True) + + +def test_support_user_can_refresh_services_for_any_office( + app, internal_nonqtxn_client, seeded_data +): + """Assert that SUPPORT users can refresh service lists outside their home office.""" + promote_internal_csr_to_support(app) + + response = internal_nonqtxn_client.get( + f"/services/refresh/?office_id={seeded_data['office_ids']['limited_office']}" + ) + body = json_of(response) + + assert_json_response(response, 200) + assert body["office_id"] == seeded_data["office_ids"]["limited_office"] diff --git a/api/app/tests/flows/test_service_request_flows.py b/api/app/tests/flows/test_service_request_flows.py new file mode 100644 index 000000000..ba27b3ea0 --- /dev/null +++ b/api/app/tests/flows/test_service_request_flows.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +import re + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_citizen, + create_service_ready_citizen, + json_of, +) + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _service_request_payload( + citizen_id: int, + seeded_data, + *, + service_id: int, + channel_id_key: str = "phone", + quantity: int = 1, +): + return { + "service_request": { + "citizen_id": citizen_id, + "service_id": service_id, + "channel_id": seeded_data["channel_ids"][channel_id_key], + "quantity": quantity, + } + } + + +def test_service_request_create_rejects_missing_payload(internal_ga_client): + """Assert that the create endpoint returns a stable 400 when JSON input is missing.""" + response = internal_ga_client.post( + "/service_requests/", + data="null", + content_type="application/json", + ) + + assert response.status_code == 400, response.get_data(as_text=True) + assert json_of(response)["message"] == "No input data received for creating service request" + + +def test_service_request_create_rejects_category_selection( + internal_ga_client, seeded_data +): + """Assert that category ids are rejected so the frontend must submit a concrete service.""" + citizen = create_citizen(internal_ga_client, 0, name="Category Rejection Citizen") + response = internal_ga_client.post( + "/service_requests/", + json=_service_request_payload( + citizen["citizen_id"], + seeded_data, + service_id=seeded_data["service_ids"]["ptax_category"], + ), + ) + + assert response.status_code == 400, response.get_data(as_text=True) + assert "category" in json_of(response)["message"].lower() + + +def test_first_service_request_assigns_ticket_numbers_and_choose_service_event( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that the first service request creates a ticket number and emits only the choose-service event.""" + from app.resources.theq import service_requests_list as service_requests_module + + choose_service_calls = [] + snowplow_events = [] + monkeypatch.setattr( + service_requests_module.SnowPlow, + "choose_service", + staticmethod( + lambda service_request, csr, event: choose_service_calls.append( + (service_request.sr_number, csr.username, event) + ) + ), + ) + monkeypatch.setattr( + service_requests_module.SnowPlow, + "snowplow_event", + staticmethod( + lambda citizen_id, csr, event, current_sr_number=None: snowplow_events.append( + (citizen_id, csr.username, event, current_sr_number) + ) + ), + ) + + citizen = create_citizen(internal_ga_client, 0, name="First Service Citizen") + response = internal_ga_client.post( + "/service_requests/", + json=_service_request_payload( + citizen["citizen_id"], + seeded_data, + service_id=seeded_data["service_ids"]["ptax"], + ), + ) + body = json_of(response) + + assert_json_response(response, 201) + assert body["service_request"]["sr_number"] == 1 + assert choose_service_calls == [(1, "cfms-postman-operator", "chooseservice")] + assert snowplow_events == [] + + with app.app_context(): + from app.models.theq import Citizen, Service + + citizen_model = Citizen.find_citizen_by_id(citizen["citizen_id"]) + service = Service.query.filter_by(service_id=seeded_data["service_ids"]["ptax"]).first() + + assert citizen_model.cs.cs_state_name == "Active" + assert citizen_model.ticket_number.startswith(service.prefix) + assert re.fullmatch(rf"{re.escape(service.prefix)}\d+", citizen_model.ticket_number) + + +def test_additional_service_request_completes_the_previous_request_and_emits_transition_events( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that adding a second service closes the active request and emits the stop/additional Snowplow events.""" + from app.resources.theq import service_requests_list as service_requests_module + + choose_service_calls = [] + snowplow_events = [] + monkeypatch.setattr( + service_requests_module.SnowPlow, + "choose_service", + staticmethod( + lambda service_request, csr, event: choose_service_calls.append( + (service_request.sr_number, event) + ) + ), + ) + monkeypatch.setattr( + service_requests_module.SnowPlow, + "snowplow_event", + staticmethod( + lambda citizen_id, csr, event, current_sr_number=None: snowplow_events.append( + (event, current_sr_number) + ) + ), + ) + + citizen, first_service = create_service_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Additional Service Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + ) + choose_service_calls.clear() + snowplow_events.clear() + + response = internal_ga_client.post( + "/service_requests/", + json=_service_request_payload( + citizen["citizen_id"], + seeded_data, + service_id=seeded_data["service_ids"]["msp"], + channel_id_key="email", + ), + ) + body = json_of(response) + + assert_json_response(response, 201) + assert body["service_request"]["sr_number"] == 2 + assert choose_service_calls == [(2, "chooseservice")] + assert snowplow_events == [("stopservice", 1), ("additionalservice", 2)] + + with app.app_context(): + from app.models.theq import ServiceReq + + requests = ( + ServiceReq.query.filter_by(citizen_id=citizen["citizen_id"]) + .order_by(ServiceReq.sr_number) + .all() + ) + + assert first_service["sr_id"] == requests[0].sr_id + assert [request.sr_state.sr_code for request in requests] == ["Complete", "Active"] + assert requests[1].sr_number == 2 diff --git a/api/app/tests/flows/test_walkin_smartboard_flows.py b/api/app/tests/flows/test_walkin_smartboard_flows.py new file mode 100644 index 000000000..8f54b20b9 --- /dev/null +++ b/api/app/tests/flows/test_walkin_smartboard_flows.py @@ -0,0 +1,377 @@ +from __future__ import annotations + +import re +from datetime import datetime, timedelta, timezone + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_queue_ready_citizen, + json_of, +) +from app.tests.auth.auth_support import create_walkin_target + +pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] + + +def _configure_office( + app, + office_id: int, + *, + currently_waiting: int | None = None, + automatic_reminder_at: int | None = None, +) -> int: + with app.app_context(): + from app.models.theq import Office + from qsystem import db + + office = Office.find_by_id(office_id) + if currently_waiting is not None: + office.currently_waiting = currently_waiting + if automatic_reminder_at is not None: + office.automatic_reminder_at = automatic_reminder_at + db.session.add(office) + db.session.commit() + return office.office_number + + +def _insert_current_agenda_panel_appointment(app, seeded_data) -> int: + with app.app_context(): + from app.models.bookings import Appointment + from qsystem import db + + start_time = datetime.now(timezone.utc).replace(microsecond=0) + timedelta( + minutes=5 + ) + appointment = Appointment( + office_id=seeded_data["office_ids"]["test_office"], + service_id=seeded_data["service_ids"]["ptax"], + start_time=start_time, + end_time=start_time + timedelta(minutes=30), + citizen_name="Agenda Panel Citizen", + contact_information="agenda@example.com", + blackout_flag="N", + is_draft=False, + stat_flag=False, + ) + db.session.add(appointment) + db.session.commit() + return appointment.appointment_id + + +def _configure_walkin_citizen( + app, + citizen_id: int, + *, + notification_phone: str | None = None, + notification_email: str | None = None, + start_position: int | None = None, + start_time: datetime | None = None, +): + with app.app_context(): + from app.models.theq import Citizen + from qsystem import db + + citizen = Citizen.find_citizen_by_id(citizen_id) + if notification_phone is not None: + citizen.notification_phone = notification_phone + if notification_email is not None: + citizen.notification_email = notification_email + if start_position is not None: + citizen.start_position = start_position + if start_time is not None: + citizen.start_time = start_time + db.session.add(citizen) + db.session.commit() + + +def _place_citizen_on_hold(internal_ga_client, citizen_id: int): + begin_response = internal_ga_client.post(f"/citizens/{citizen_id}/begin_service/") + hold_response = internal_ga_client.post(f"/citizens/{citizen_id}/place_on_hold/") + assert_json_response(begin_response, 200) + assert_json_response(hold_response, 200) + + +def test_walkin_lookup_groups_booked_agenda_and_walkin_entries( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that walk-in lookups keep booked, agenda, and walk-in entries in frontend order.""" + create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Booked Queue Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="booked|||appointment", + ) + target_citizen, walkin_id = create_walkin_target( + app, internal_ga_client, seeded_data + ) + _insert_current_agenda_panel_appointment(app, seeded_data) + + response = bare_client.get(f"/citizen/all-walkin/{walkin_id}/") + body = json_of(response) + + assert_json_response(response, 200) + assert [entry["flag"] for entry in body["citizen"]] == [ + "booked_app", + "agenda_panel", + "walkin_app", + ] + assert body["citizen"][0]["ticket_number"] + assert re.fullmatch( + r"\d{2}/\d{2}/\d{4}, \d{2}:\d{2}:\d{2}", body["citizen"][1]["start_time"] + ) + assert body["citizen"][2]["walkin_unique_id"] == walkin_id + assert body["show_estimate"] in {True, False} + assert target_citizen["citizen_id"] > 0 + + +def test_waiting_queue_details_group_booked_and_walkin_rows( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that waiting queue details preserve the booked-then-walkin grouping the smartboard renders.""" + create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Booked Queue Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + comments="booked|||appointment", + ) + walkin_citizen, _walkin_id = create_walkin_target(app, internal_ga_client, seeded_data) + office_number = _configure_office( + app, seeded_data["office_ids"]["test_office"], currently_waiting=1 + ) + + response = bare_client.get(f"/smardboard/Q-details/waiting/{office_number}") + body = json_of(response) + + assert_json_response(response, 200) + assert [entry["flag"] for entry in body["citizen_in_q"]] == [ + "booked_app", + "walkin_app", + ] + assert body["citizen_in_q"][0]["service_name"] == "Property Tax" + assert body["citizen_in_q"][1]["citizen_id"] == walkin_citizen["citizen_id"] + + +def test_upcoming_queue_details_return_localized_agenda_panel_rows( + bare_client, seeded_data, app +): + """Assert that upcoming queue details expose agenda-panel rows in display-ready format.""" + office_number = _configure_office( + app, seeded_data["office_ids"]["test_office"], currently_waiting=1 + ) + _insert_current_agenda_panel_appointment(app, seeded_data) + + response = bare_client.get(f"/smardboard/Q-details/upcoming/{office_number}") + body = json_of(response) + + assert_json_response(response, 200) + assert len(body["booked_not_checkin"]) == 1 + assert body["booked_not_checkin"][0]["flag"] == "agenda_panel" + assert re.fullmatch( + r"\d{2}/\d{2}/\d{4}, \d{2}:\d{2}:\d{2}", + body["booked_not_checkin"][0]["start_time"], + ) + + +def test_smartboard_returns_waiting_tickets_and_active_periods( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that the public smartboard response carries ticket numbers and waiting-state periods.""" + _citizen, _service_request, queued_citizen = create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Smartboard Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + office_number = _configure_office( + app, seeded_data["office_ids"]["test_office"], currently_waiting=1 + ) + + response = bare_client.get(f"/smartboard/?office_number={office_number}") + body = json_of(response) + + assert_json_response(response, 200) + waiting_entry = next( + citizen + for citizen in body["citizens"] + if citizen["ticket_number"] == queued_citizen["ticket_number"] + ) + assert waiting_entry["active_period"]["ps"]["ps_name"] == "Waiting" + + +def test_send_line_walkin_reminder_updates_notification_flags( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that walk-in reminders flip the persisted reminder flags once the SMS path runs.""" + from app.resources.bookings.walkin import walkin as walkin_module + + monkeypatch.setattr( + walkin_module, "send_walkin_reminder_sms", lambda *args, **kwargs: True + ) + walkin_citizen, _walkin_id = create_walkin_target( + app, internal_ga_client, seeded_data + ) + _configure_office( + app, + seeded_data["office_ids"]["test_office"], + automatic_reminder_at=1, + ) + + response = internal_ga_client.post( + "/send-reminder/line-walkin/", + json={"previous_citizen_id": walkin_citizen["citizen_id"]}, + ) + + assert_json_response(response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + citizen = Citizen.find_citizen_by_id(walkin_citizen["citizen_id"]) + assert citizen.automatic_reminder_flag == 1 + assert citizen.reminder_flag == 1 + assert citizen.notification_sent_time is not None + + +def test_walkin_lookup_includes_later_walkins_when_the_target_citizen_is_on_hold( + bare_client, internal_ga_client, seeded_data, app +): + """Assert that on-hold citizens continue to see later walk-ins in the lookup payload.""" + target_citizen, walkin_id = create_walkin_target(app, internal_ga_client, seeded_data) + later_citizen, later_walkin_id = create_walkin_target( + app, internal_ga_client, seeded_data + ) + _configure_walkin_citizen( + app, + later_citizen["citizen_id"], + start_time=datetime.now(timezone.utc) + timedelta(minutes=30), + ) + + initial_response = bare_client.get(f"/citizen/all-walkin/{walkin_id}/") + initial_walkin_ids = { + entry.get("walkin_unique_id") + for entry in json_of(initial_response)["citizen"] + if entry.get("flag") == "walkin_app" + } + assert later_walkin_id not in initial_walkin_ids + + _place_citizen_on_hold(internal_ga_client, target_citizen["citizen_id"]) + held_response = bare_client.get(f"/citizen/all-walkin/{walkin_id}/") + held_walkin_ids = { + entry.get("walkin_unique_id") + for entry in json_of(held_response)["citizen"] + if entry.get("flag") == "walkin_app" + } + + assert_json_response(held_response, 200) + assert later_citizen["citizen_id"] > 0 + assert later_walkin_id in held_walkin_ids + + +def test_send_line_walkin_reminder_skips_notifications_before_the_threshold_position( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that reminder side effects stay off until a citizen reaches the configured threshold.""" + first_citizen, _first_walkin_id = create_walkin_target(app, internal_ga_client, seeded_data) + second_citizen, _second_walkin_id = create_walkin_target( + app, internal_ga_client, seeded_data + ) + _configure_walkin_citizen(app, second_citizen["citizen_id"], start_position=1) + _configure_office( + app, + seeded_data["office_ids"]["test_office"], + automatic_reminder_at=2, + ) + + monkeypatch.setattr( + "app.resources.bookings.walkin.walkin.send_walkin_reminder_sms", + lambda *args, **kwargs: (_ for _ in ()).throw( + AssertionError("SMS reminder should not have been sent") + ), + ) + monkeypatch.setattr( + "app.resources.bookings.walkin.walkin.send_email", + lambda *args, **kwargs: (_ for _ in ()).throw( + AssertionError("Email reminder should not have been sent") + ), + ) + + response = internal_ga_client.post( + "/send-reminder/line-walkin/", + json={"previous_citizen_id": first_citizen["citizen_id"]}, + ) + + assert_json_response(response, 200) + + with app.app_context(): + from app.models.theq import Citizen + + refreshed = Citizen.find_citizen_by_id(second_citizen["citizen_id"]) + assert refreshed.automatic_reminder_flag in {None, 0} + assert refreshed.reminder_flag in {None, 0} + assert refreshed.notification_sent_time is None + + +def test_send_line_walkin_reminder_uses_email_when_no_phone_number_is_available( + internal_ga_client, seeded_data, app, monkeypatch +): + """Assert that the email reminder branch updates the persisted flags when SMS is unavailable.""" + email_calls = [] + monkeypatch.setattr( + "app.resources.bookings.walkin.walkin.get_walkin_reminder_email_contents", + lambda citizen, office: ("walkin@example.com", "Reminder", "Body"), + ) + monkeypatch.setattr( + "app.resources.bookings.walkin.walkin.send_email", + lambda token, *args: email_calls.append((token, args)), + ) + + walkin_citizen, _walkin_id = create_walkin_target( + app, internal_ga_client, seeded_data + ) + _configure_walkin_citizen( + app, + walkin_citizen["citizen_id"], + notification_phone="", + notification_email="walkin@example.com", + start_position=1, + ) + _configure_office( + app, + seeded_data["office_ids"]["test_office"], + automatic_reminder_at=1, + ) + + response = internal_ga_client.post( + "/send-reminder/line-walkin/", + json={"previous_citizen_id": walkin_citizen["citizen_id"]}, + ) + + assert_json_response(response, 200) + assert len(email_calls) == 1 + assert email_calls[0][0] == "theq-test-token" + + with app.app_context(): + from app.models.theq import Citizen + + citizen = Citizen.find_citizen_by_id(walkin_citizen["citizen_id"]) + assert citizen.automatic_reminder_flag == 1 + assert citizen.reminder_flag == 1 + assert citizen.notification_sent_time is not None diff --git a/api/app/tests/helpers/__init__.py b/api/app/tests/helpers/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/api/app/tests/helpers/__init__.py @@ -0,0 +1 @@ + diff --git a/api/app/tests/helpers/appointments.py b/api/app/tests/helpers/appointments.py new file mode 100644 index 000000000..4ec17fd65 --- /dev/null +++ b/api/app/tests/helpers/appointments.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from datetime import datetime, timedelta +from zoneinfo import ZoneInfo + +from app.tests.api_test_support import ( + assert_json_response, + create_public_user, + future_utc_window, + json_of, + unique_name, +) + + +def align_current_pacific_time_for_appointment( + monkeypatch, appointment_start_time: str, timezone_name: str +): + appointment_local = datetime.fromisoformat( + appointment_start_time.replace("Z", "+00:00") + ).astimezone(ZoneInfo(timezone_name)) + now_local = appointment_local - timedelta(days=1) + monkeypatch.setattr( + "app.models.bookings.appointments.current_pacific_time", lambda: now_local + ) + + +def configure_public_user_reminders( + public_client, + *, + email: str, + telephone: str | None = None, + send_email_reminders: bool = False, + send_sms_reminders: bool = False, +) -> dict: + user = create_public_user(public_client) + response = public_client.put( + f"/users/{user['user_id']}/", + json={ + "email": email, + "telephone": telephone, + "send_email_reminders": send_email_reminders, + "send_sms_reminders": send_sms_reminders, + }, + ) + assert_json_response(response, 200) + return json_of(response)[0] + + +def create_internal_reminder_appointment( + api_client, + seeded_data, + *, + contact_information: str, + days_from_now: int = 2, + citizen_name: str = "Reminder Appointment", +) -> dict: + start_time, end_time = future_utc_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Reminder coverage", + "citizen_name": unique_name(citizen_name), + "contact_information": contact_information, + }, + ) + assert_json_response(response, 201) + return json_of(response)["appointment"] diff --git a/api/app/tests/helpers/exams.py b/api/app/tests/helpers/exams.py new file mode 100644 index 000000000..d80f2e0a8 --- /dev/null +++ b/api/app/tests/helpers/exams.py @@ -0,0 +1,30 @@ +from __future__ import annotations + + +def seed_exam_bcmp_job(app, exam_id: int, *, bcmp_job_id: str, upload_received_ind: int): + with app.app_context(): + from app.models.bookings import Exam + from qsystem import db + + exam = Exam.query.filter_by(exam_id=exam_id).first() + exam.bcmp_job_id = bcmp_job_id + exam.upload_received_ind = upload_received_ind + db.session.add(exam) + db.session.commit() + return exam.exam_id + + +def exam_upload_received_ind(app, exam_id: int) -> int | None: + with app.app_context(): + from app.models.bookings import Exam + + exam = Exam.query.filter_by(exam_id=exam_id).first() + return exam.upload_received_ind + + +def exam_invigilator_id(app, exam_id: int) -> int | None: + with app.app_context(): + from app.models.bookings import Exam + + exam = Exam.query.filter_by(exam_id=exam_id).first() + return exam.invigilator_id diff --git a/api/app/tests/test_api_contracts.py b/api/app/tests/test_api_contracts.py deleted file mode 100644 index 756114659..000000000 --- a/api/app/tests/test_api_contracts.py +++ /dev/null @@ -1,74 +0,0 @@ -import pytest - -from app.tests.api_test_support import assert_status, json_of - - -pytestmark = [pytest.mark.contracts, pytest.mark.usefixtures("seeded_database")] - - -def test_health_and_readyz_endpoints(client): - health_response = client.get("/api/v1/healthz/") - ready_response = client.get("/api/v1/readyz/") - - assert_status(health_response, 200) - assert_status(ready_response, 200) - assert json_of(health_response)["message"] == "api is healthy" - assert json_of(ready_response)["message"] == "api is ready" - - -def test_internal_contract_endpoints(internal_ga_client, seeded_data): - del seeded_data - - channels_response = internal_ga_client.get("/channels/") - categories_response = internal_ga_client.get("/categories/") - services_response = internal_ga_client.get("/services/") - offices_response = internal_ga_client.get("/offices/") - rooms_response = internal_ga_client.get("/rooms/") - invigilators_response = internal_ga_client.get("/invigilators/") - exam_types_response = internal_ga_client.get("/exam_types/") - csr_self_response = internal_ga_client.get("/csrs/me/") - - for response in ( - channels_response, - categories_response, - services_response, - offices_response, - rooms_response, - invigilators_response, - exam_types_response, - csr_self_response, - ): - assert_status(response, 200) - - channels = json_of(channels_response)["channels"] - categories = json_of(categories_response)["categories"] - services = json_of(services_response)["services"] - offices = json_of(offices_response)["offices"] - rooms = json_of(rooms_response)["rooms"] - invigilators = json_of(invigilators_response)["invigilators"] - exam_types = json_of(exam_types_response)["exam_types"] - csr_self = json_of(csr_self_response) - - assert any(channel["channel_name"] == "Phone" for channel in channels) - assert any(category["service_name"] == "Property Tax" for category in categories) - assert any(service["service_name"] == "Payment - MSP" for service in services) - assert any(office["office_name"] == "Test Office" for office in offices) - assert any(room["room_name"] == "Boardroom 1" for room in rooms) - assert any(invigilator["invigilator_name"] == "Homer Simpson" for invigilator in invigilators) - assert any(exam_type["exam_type_name"] for exam_type in exam_types) - assert csr_self["csr"]["role"]["role_code"] == "GA" - assert csr_self["csr"]["office"]["office_name"] == "Test Office" - - -def test_public_user_profile_contracts(public_client): - create_response = public_client.post("/users/") - assert_status(create_response, 200) - - created_user = json_of(create_response)[0] - get_me_response = public_client.get("/users/me/") - appointments_response = public_client.get("/users/appointments/") - - assert_status(get_me_response, 200) - assert_status(appointments_response, 200) - assert json_of(get_me_response)[0]["username"] == created_user["username"] - assert "appointments" in json_of(appointments_response) diff --git a/api/app/tests/test_appointment_flows.py b/api/app/tests/test_appointment_flows.py deleted file mode 100644 index 1d655c7d9..000000000 --- a/api/app/tests/test_appointment_flows.py +++ /dev/null @@ -1,168 +0,0 @@ -from __future__ import annotations - -from datetime import datetime, timedelta, timezone -from typing import Optional -from uuid import uuid4 - -import pytest - -from app.tests.api_test_support import ( - assert_status, - first_day_with_slots, - json_of, - slot_window_to_iso, - unique_name, -) - - -pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] - - -def _future_window(days_from_now: int, duration_minutes: int = 30) -> tuple[str, str]: - start = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + timedelta(days=days_from_now) - end = start + timedelta(minutes=duration_minutes) - return start.isoformat(), end.isoformat() - - -def _create_internal_appointment(api_client, seeded_data, *, days_from_now: int, recurring_uuid: Optional[str] = None): - start_time, end_time = _future_window(days_from_now) - response = api_client.post( - "/appointments/", - json={ - "service_id": seeded_data["service_ids"]["msp"], - "office_id": seeded_data["office_ids"]["test_office"], - "start_time": start_time, - "end_time": end_time, - "comments": "Internal appointment", - "citizen_name": unique_name("internal-appt"), - "contact_information": "internal@example.com", - "recurring_uuid": recurring_uuid, - }, - ) - assert_status(response, 201) - return json_of(response)["appointment"] - - -def _public_slot_payload(public_client, seeded_data, *, minimum_slots: int = 1): - slots_response = public_client.get( - f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" - ) - assert_status(slots_response, 200) - day_key, slots = first_day_with_slots(json_of(slots_response), minimum_slots=minimum_slots) - start_time, end_time = slot_window_to_iso(day_key, slots[0], seeded_data["office_timezones"]["limited_office"]) - return { - "service_id": seeded_data["service_ids"]["limited_office_service"], - "office_id": seeded_data["office_ids"]["limited_office"], - "start_time": start_time, - "end_time": end_time, - "comments": "Public appointment", - "citizen_name": "Codex Public User", - "contact_information": "public@example.com", - }, day_key, slots - - -def test_internal_appointment_crud_and_recurring_workflows(internal_ga_client, seeded_data): - appointment = _create_internal_appointment(internal_ga_client, seeded_data, days_from_now=2) - - list_response = internal_ga_client.get("/appointments/") - detail_response = internal_ga_client.get(f"/appointments/{appointment['appointment_id']}/") - update_response = internal_ga_client.put( - f"/appointments/{appointment['appointment_id']}/", - json={"comments": "Internal appointment updated"}, - ) - delete_response = internal_ga_client.delete(f"/appointments/{appointment['appointment_id']}/") - - assert_status(list_response, 200) - assert_status(detail_response, 200) - assert_status(update_response, 200) - assert_status(delete_response, 204) - assert any(item["appointment_id"] == appointment["appointment_id"] for item in json_of(list_response)["appointments"]) - assert json_of(update_response)["appointment"]["comments"] == "Internal appointment updated" - - recurring_uuid = str(uuid4()) - first_recurring = _create_internal_appointment( - internal_ga_client, - seeded_data, - days_from_now=3, - recurring_uuid=recurring_uuid, - ) - second_recurring = _create_internal_appointment( - internal_ga_client, - seeded_data, - days_from_now=4, - recurring_uuid=recurring_uuid, - ) - - recurring_update = internal_ga_client.put( - f"/appointments/recurring/{recurring_uuid}", - json={"comments": "Recurring appointment updated"}, - ) - recurring_delete = internal_ga_client.delete(f"/appointments/recurring/{recurring_uuid}") - final_list = internal_ga_client.get("/appointments/") - - assert_status(recurring_update, 200) - assert_status(recurring_delete, 204) - assert_status(final_list, 200) - remaining_ids = {item["appointment_id"] for item in json_of(final_list)["appointments"]} - assert first_recurring["appointment_id"] not in remaining_ids - assert second_recurring["appointment_id"] not in remaining_ids - - -def test_public_user_profile_and_appointment_workflows(public_client, seeded_data): - create_user_response = public_client.post("/users/") - assert_status(create_user_response, 200) - created_user = json_of(create_user_response)[0] - - update_user_response = public_client.put( - f"/users/{created_user['user_id']}/", - json={ - "email": "updated-public@example.com", - "telephone": "2505550100", - "send_email_reminders": True, - "send_sms_reminders": True, - }, - ) - get_me_response = public_client.get("/users/me/") - - assert_status(update_user_response, 200) - assert_status(get_me_response, 200) - assert json_of(update_user_response)[0]["email"] == "updated-public@example.com" - - appointment_payload, day_key, slots = _public_slot_payload(public_client, seeded_data, minimum_slots=2) - create_appointment_response = public_client.post("/appointments/", json=appointment_payload) - assert_status(create_appointment_response, 201) - appointment = json_of(create_appointment_response)["appointment"] - - list_response = public_client.get("/users/appointments/") - assert_status(list_response, 200) - assert any(item["appointment_id"] == appointment["appointment_id"] for item in json_of(list_response)["appointments"]) - - second_start_time, second_end_time = slot_window_to_iso( - day_key, - slots[1], - seeded_data["office_timezones"]["limited_office"], - ) - max_limit_response = public_client.post( - "/appointments/", - json={ - **appointment_payload, - "start_time": second_start_time, - "end_time": second_end_time, - }, - ) - assert_status(max_limit_response, 400) - assert json_of(max_limit_response)["code"] == "MAX_NO_OF_APPOINTMENTS_REACHED" - - delete_response = public_client.delete(f"/appointments/{appointment['appointment_id']}/") - assert_status(delete_response, 204) - - -def test_appointment_slots_endpoint(public_client, seeded_data): - response = public_client.get( - f"/offices/{seeded_data['office_ids']['limited_office']}/slots/?service_id={seeded_data['service_ids']['limited_office_service']}" - ) - - assert_status(response, 200) - day_key, slots = first_day_with_slots(json_of(response)) - assert day_key - assert slots[0]["no_of_slots"] >= 1 diff --git a/api/app/tests/test_availability_service.py b/api/app/tests/test_availability_service.py new file mode 100644 index 000000000..3ea0ebe1b --- /dev/null +++ b/api/app/tests/test_availability_service.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime, time, timedelta, timezone +import importlib +from zoneinfo import ZoneInfo + +import pytest +from app.utilities.yesno import YesNo + +pytestmark = [pytest.mark.flows, pytest.mark.integration] + + +@dataclass +class FakeTimezone: + timezone_name: str + + +@dataclass +class FakeTimeslot: + day_of_week: list[str] + start_time: time + end_time: time + no_of_slots: int + + +@dataclass +class FakeService: + is_dlkt: object = None + timeslot_duration: int | None = None + + +@dataclass +class FakeOffice: + office_id: int + timezone: FakeTimezone + timeslots: list[FakeTimeslot] + appointments_enabled_ind: int = 1 + appointment_duration: int = 30 + soonest_appointment: int = 0 + number_of_dlkt: int = 0 + + +@dataclass +class FakeAppointment: + start_time: datetime + end_time: datetime + blackout_flag: str = "N" + stat_flag: bool = False + service: FakeService | None = None + + +def _freeze_now(monkeypatch, frozen_now: datetime): + availability_module = importlib.import_module("app.services.availability_service") + + class FrozenDateTime(datetime): + @classmethod + def now(cls, tz=None): + if tz is None: + return frozen_now + return frozen_now.astimezone(tz) + + monkeypatch.setattr(availability_module.datetime, "datetime", FrozenDateTime) + + +def _patch_appointments(monkeypatch, appointments: list[FakeAppointment]): + availability_module = importlib.import_module("app.services.availability_service") + monkeypatch.setattr( + availability_module.Appointment, + "find_appointment_availability", + lambda **kwargs: appointments, + ) + + +def _local_day(year: int, month: int, day: int, timezone_name: str) -> datetime: + return datetime(year, month, day, tzinfo=ZoneInfo(timezone_name)) + + +def _office( + *, + timezone_name: str = "America/Vancouver", + day_names: list[str] | None = None, + start_hour: int = 9, + end_hour: int = 12, + no_of_slots: int = 1, + appointment_duration: int = 30, + soonest_appointment: int = 0, + number_of_dlkt: int = 0, +) -> FakeOffice: + if day_names is None: + day_names = ["Wednesday"] + return FakeOffice( + office_id=1, + timezone=FakeTimezone(timezone_name), + timeslots=[ + FakeTimeslot( + day_of_week=day_names, + start_time=time(start_hour, 0), + end_time=time(end_hour, 0), + no_of_slots=no_of_slots, + ) + ], + appointment_duration=appointment_duration, + soonest_appointment=soonest_appointment, + number_of_dlkt=number_of_dlkt, + ) + + +def test_group_appointments_keeps_local_times_across_dst_boundaries(app): + """Assert that grouped appointment rows preserve local wall-clock times across DST changes.""" + del app + AvailabilityService = importlib.import_module( + "app.services.availability_service" + ).AvailabilityService + appointments = [ + FakeAppointment( + start_time=datetime(2024, 3, 10, 9, 30, tzinfo=timezone.utc), + end_time=datetime(2024, 3, 10, 10, 0, tzinfo=timezone.utc), + ), + FakeAppointment( + start_time=datetime(2024, 3, 10, 10, 30, tzinfo=timezone.utc), + end_time=datetime(2024, 3, 10, 11, 0, tzinfo=timezone.utc), + ), + ] + + grouped = AvailabilityService.group_appointments(appointments, "America/Vancouver") + + assert "03/10/2024" in grouped + assert [slot["start_time"].strftime("%H:%M") for slot in grouped["03/10/2024"]] == [ + "01:30", + "03:30", + ] + + +def test_get_available_slots_respects_soonest_appointment_cutoffs(app, monkeypatch): + """Assert that slots earlier than the office soonest-appointment window are suppressed.""" + del app + AvailabilityService = importlib.import_module( + "app.services.availability_service" + ).AvailabilityService + _freeze_now(monkeypatch, datetime(2024, 7, 10, 9, 0, tzinfo=timezone.utc)) + _patch_appointments(monkeypatch, []) + + office = _office(timezone_name="UTC", soonest_appointment=90) + slots = AvailabilityService.get_available_slots( + office, + [_local_day(2024, 7, 10, office.timezone.timezone_name)], + ) + + assert [slot["start_time"] for slot in slots["07/10/2024"]] == [ + "10:30", + "11:00", + "11:30", + ] + + +def test_get_available_slots_prunes_blackout_rows(app, monkeypatch): + """Assert that blackout appointments remove conflicting slots from the availability map.""" + del app + AvailabilityService = importlib.import_module( + "app.services.availability_service" + ).AvailabilityService + _freeze_now(monkeypatch, datetime(2024, 7, 10, 8, 0, tzinfo=timezone.utc)) + _patch_appointments( + monkeypatch, + [ + FakeAppointment( + start_time=datetime(2024, 7, 10, 10, 0, tzinfo=timezone.utc), + end_time=datetime(2024, 7, 10, 10, 30, tzinfo=timezone.utc), + blackout_flag="Y", + ) + ], + ) + + office = _office(timezone_name="UTC", end_hour=11, no_of_slots=1) + slots = AvailabilityService.get_available_slots( + office, + [_local_day(2024, 7, 10, office.timezone.timezone_name)], + ) + + assert [slot["start_time"] for slot in slots["07/10/2024"]] == [ + "09:00", + "09:30", + "10:30", + ] + + +def test_has_available_slots_rejects_overlaps_but_allows_open_following_windows( + app, monkeypatch +): + """Assert that conflict checks fail for occupied windows and pass for later open windows.""" + del app + AvailabilityService = importlib.import_module( + "app.services.availability_service" + ).AvailabilityService + _freeze_now(monkeypatch, datetime(2024, 7, 10, 8, 0, tzinfo=timezone.utc)) + _patch_appointments( + monkeypatch, + [ + FakeAppointment( + start_time=datetime(2024, 7, 10, 9, 0, tzinfo=timezone.utc), + end_time=datetime(2024, 7, 10, 10, 0, tzinfo=timezone.utc), + ) + ], + ) + + office = _office( + timezone_name="UTC", end_hour=11, no_of_slots=1, appointment_duration=60 + ) + service = FakeService() + + blocked = AvailabilityService.has_available_slots( + office, + datetime(2024, 7, 10, 9, 30, tzinfo=timezone.utc), + datetime(2024, 7, 10, 9, 45, tzinfo=timezone.utc), + service, + ) + open_window = AvailabilityService.has_available_slots( + office, + datetime(2024, 7, 10, 10, 0, tzinfo=timezone.utc), + datetime(2024, 7, 10, 10, 30, tzinfo=timezone.utc), + service, + ) + + assert blocked is False + assert open_window is True + + +def test_get_available_slots_caps_dlkt_capacity_to_the_office_limit( + app, monkeypatch +): + """Assert that DLKT availability honors the office-specific DLKT cap per slot.""" + del app + AvailabilityService = importlib.import_module( + "app.services.availability_service" + ).AvailabilityService + _freeze_now(monkeypatch, datetime(2024, 7, 10, 8, 0, tzinfo=timezone.utc)) + _patch_appointments(monkeypatch, []) + + office = _office(timezone_name="UTC", no_of_slots=3, number_of_dlkt=1) + dlkt_service = FakeService(is_dlkt=YesNo.YES) + slots = AvailabilityService.get_available_slots( + office, + [_local_day(2024, 7, 10, office.timezone.timezone_name)], + service=dlkt_service, + ) + + assert {slot["no_of_slots"] for slot in slots["07/10/2024"]} == {1} diff --git a/api/app/tests/test_booking_exam_flows.py b/api/app/tests/test_booking_exam_flows.py deleted file mode 100644 index ea0209f39..000000000 --- a/api/app/tests/test_booking_exam_flows.py +++ /dev/null @@ -1,189 +0,0 @@ -from __future__ import annotations - -from datetime import datetime, timedelta, timezone -from typing import Optional -from uuid import uuid4 -from zoneinfo import ZoneInfo - -import pytest - -from app.tests.api_test_support import assert_status, json_of, unique_name - - -pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] - - -def _future_window(days_from_now: int, duration_hours: int = 2) -> tuple[str, str]: - start = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + timedelta(days=days_from_now) - end = start + timedelta(hours=duration_hours) - return start.isoformat(), end.isoformat() - - -def _create_booking(api_client, seeded_data, *, days_from_now: int, recurring_uuid: Optional[str] = None): - start_time, end_time = _future_window(days_from_now) - response = api_client.post( - "/bookings/", - json={ - "booking_name": unique_name("booking"), - "booking_contact_information": "booking@example.com", - "fees": "false", - "office_id": seeded_data["office_ids"]["test_office"], - "room_id": seeded_data["room_id"], - "start_time": start_time, - "end_time": end_time, - "recurring_uuid": recurring_uuid, - "invigilator_id": [seeded_data["invigilator_ids"][0]], - }, - ) - assert_status(response, 201) - return json_of(response)["booking"] - - -def _create_exam(api_client, seeded_data, booking_id: int, *, event_id: str): - expiry_date = (datetime.now(timezone.utc) + timedelta(days=30)).replace(microsecond=0).isoformat() - response = api_client.post( - "/exams/", - json={ - "booking_id": booking_id, - "event_id": event_id, - "exam_method": "paper", - "exam_name": unique_name("exam"), - "exam_type_id": seeded_data["exam_type_id"], - "exam_written_ind": 0, - "examinee_name": "Codex Examinee", - "notes": "Codex exam notes", - "number_of_students": 19, - "office_id": seeded_data["office_ids"]["test_office"], - "offsite_location": "Test Office", - "expiry_date": expiry_date, - }, - ) - assert_status(response, 201) - return json_of(response)["exam"] - - -def test_booking_crud_and_recurring_workflows(internal_ga_client, seeded_data): - booking = _create_booking(internal_ga_client, seeded_data, days_from_now=2) - - list_response = internal_ga_client.get("/bookings/") - detail_response = internal_ga_client.get(f"/bookings/{booking['booking_id']}/") - update_response = internal_ga_client.put( - f"/bookings/{booking['booking_id']}/", - json={ - "booking_name": "Updated single booking", - "booking_contact_information": "updated-booking@example.com", - "invigilator_id": [seeded_data["invigilator_ids"][1]], - }, - ) - delete_response = internal_ga_client.delete(f"/bookings/{booking['booking_id']}/") - - assert_status(list_response, 200) - assert_status(detail_response, 200) - assert_status(update_response, 200) - assert_status(delete_response, 204) - assert any(item["booking_id"] == booking["booking_id"] for item in json_of(list_response)["bookings"]) - assert json_of(update_response)["booking"]["booking_name"] == "Updated single booking" - - recurring_uuid = str(uuid4()) - first_recurring = _create_booking(internal_ga_client, seeded_data, days_from_now=3, recurring_uuid=recurring_uuid) - second_recurring = _create_booking(internal_ga_client, seeded_data, days_from_now=4, recurring_uuid=recurring_uuid) - - recurring_update = internal_ga_client.put( - f"/bookings/recurring/{recurring_uuid}", - json={"booking_name": "Recurring booking updated"}, - ) - recurring_delete = internal_ga_client.delete(f"/bookings/recurring/{recurring_uuid}") - final_list = internal_ga_client.get("/bookings/") - - assert_status(recurring_update, 200) - assert_status(recurring_delete, 204) - assert_status(final_list, 200) - final_booking_ids = {item["booking_id"] for item in json_of(final_list)["bookings"]} - assert first_recurring["booking_id"] not in final_booking_ids - assert second_recurring["booking_id"] not in final_booking_ids - - -def test_exam_crud_event_lookup_and_export(internal_ga_client, seeded_data): - booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) - event_id = str(9000 + booking["booking_id"]) - exam = _create_exam(internal_ga_client, seeded_data, booking["booking_id"], event_id=event_id) - - detail_response = internal_ga_client.get(f"/exams/{exam['exam_id']}/") - list_response = internal_ga_client.get("/exams/") - update_response = internal_ga_client.put( - f"/exams/{exam['exam_id']}/", - json={"exam_name": "Updated exam name", "notes": "Updated notes"}, - ) - event_lookup_response = internal_ga_client.get(f"/exams/event_id/{event_id}/") - - booking_start = datetime.fromisoformat(booking["start_time"].replace("Z", "+00:00")) - export_date = booking_start.astimezone( - ZoneInfo(seeded_data["office_timezones"]["test_office"]) - ).date().isoformat() - export_response = internal_ga_client.get(f"/exams/export/?start_date={export_date}&end_date={export_date}") - delete_response = internal_ga_client.delete(f"/exams/{exam['exam_id']}/") - post_delete_list = internal_ga_client.get("/exams/") - - assert_status(detail_response, 200) - assert_status(list_response, 200) - assert_status(update_response, 201) - assert_status(event_lookup_response, 200) - assert_status(export_response, 200) - assert_status(delete_response, 204) - assert_status(post_delete_list, 200) - - assert json_of(detail_response)["exam"]["event_id"] == event_id - assert any(item["exam_id"] == exam["exam_id"] for item in json_of(list_response)["exams"]) - assert json_of(update_response)["exam"]["exam_name"] == "Updated exam name" - assert json_of(event_lookup_response)["message"] is True - export_body = export_response.get_data(as_text=True) - assert "Office Name,Exam Type,Exam ID,Exam Name" in export_body - assert "Updated exam name" in export_body - assert all(item["exam_id"] != exam["exam_id"] for item in json_of(post_delete_list)["exams"]) - - -def test_invigilator_lists_and_shadow_count_mutations(internal_ga_client): - list_response = internal_ga_client.get("/invigilators/") - offsite_response = internal_ga_client.get("/invigilators/offsite/") - - assert_status(list_response, 200) - assert_status(offsite_response, 200) - - invigilators = json_of(list_response)["invigilators"] - offsite_invigilators = json_of(offsite_response)["invigilators"] - - assert invigilators - assert offsite_invigilators - - first_invigilator = invigilators[0] - assert first_invigilator["shadow_count"] == 2 - assert first_invigilator["shadow_flag"] == "Y" - assert any( - invigilator["invigilator_name"] == "Pest 1" for invigilator in offsite_invigilators - ) - - subtract_response = internal_ga_client.put( - f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=True&add=False" - ) - assert_status(subtract_response, 200) - subtracted = json_of(subtract_response)["invigilator"] - - assert subtracted["invigilator_id"] == first_invigilator["invigilator_id"] - assert subtracted["contact_email"] == first_invigilator["contact_email"] - assert subtracted["contact_phone"] == first_invigilator["contact_phone"] - assert subtracted["invigilator_notes"] == first_invigilator["invigilator_notes"] - assert subtracted["shadow_count"] == 1 - assert subtracted["shadow_flag"] == "N" - - add_response = internal_ga_client.put( - f"/invigilator/{first_invigilator['invigilator_id']}/?subtract=False&add=True" - ) - assert_status(add_response, 200) - added_back = json_of(add_response)["invigilator"] - - assert added_back["invigilator_id"] == first_invigilator["invigilator_id"] - assert added_back["contact_email"] == first_invigilator["contact_email"] - assert added_back["contact_phone"] == first_invigilator["contact_phone"] - assert added_back["invigilator_notes"] == first_invigilator["invigilator_notes"] - assert added_back["shadow_count"] == 2 - assert added_back["shadow_flag"] == "Y" diff --git a/api/app/tests/test_citizen_finish_service.py b/api/app/tests/test_citizen_finish_service.py deleted file mode 100644 index 045cd439f..000000000 --- a/api/app/tests/test_citizen_finish_service.py +++ /dev/null @@ -1,9 +0,0 @@ -def test_finish_service_query_compiles_without_loader_conflicts(app, db, migrated_database): - del migrated_database - - from app.resources.theq.citizen.citizen_finish_service import _citizen_finish_service_query - - with app.app_context(): - compiled = _citizen_finish_service_query(1).statement.compile(dialect=db.engine.dialect) - - assert "FROM citizen" in str(compiled) diff --git a/api/app/tests/test_flask3_admin.py b/api/app/tests/test_flask3_admin.py new file mode 100644 index 000000000..32945adc6 --- /dev/null +++ b/api/app/tests/test_flask3_admin.py @@ -0,0 +1,121 @@ +import inspect +from types import SimpleNamespace + +import pytest +from app.utilities.flask_admin_compat import apply_wtforms_compat +from flask_admin.contrib.sqla.fields import QuerySelectField +from flask_admin.contrib.sqla.validators import Unique +from flask_admin.form.fields import Select2Field +from flask_admin.form.validators import FieldListInputRequired +from wtforms import Form +from wtforms.fields import SelectFieldBase + + +def _unwrap(func): + return inspect.unwrap(func) + + +def test_application_url_map_contains_admin_and_healthz(app): + """Assert that the Flask 3 app still exposes the admin and health routes.""" + routes = {rule.rule for rule in app.url_map.iter_rules()} + + assert "/admin/" in routes + assert "/api/v1/healthz/" in routes + + +def test_admin_index_renders_without_bootstrap3_assets(client): + """Assert that the admin index renders without legacy Bootstrap 3 assets.""" + response = client.get("/admin/") + + assert response.status_code == 200 + + body = response.get_data(as_text=True) + + assert "Admin Console" in body + assert "bootstrap3" not in body + + +def test_login_resource_redirects_authenticated_users_to_admin(app, monkeypatch): + """Assert that authenticated login requests still redirect to the admin console.""" + from app.resources.theq import login as login_module + + fake_csr = SimpleNamespace(username="tester") + logged_in = [] + + monkeypatch.setattr(login_module, "get_username", lambda: "tester@idir") + monkeypatch.setattr(login_module.CSR, "find_by_username", lambda username: fake_csr) + monkeypatch.setattr(login_module, "login_user", lambda user: logged_in.append(user)) + + handler = _unwrap(login_module.Login.get) + + with app.test_request_context("/api/v1/login/"): + response = handler(login_module.Login()) + + assert logged_in == [fake_csr] + assert response.status_code == 302 + assert response.location.endswith("/admin/") + + +def test_apply_wtforms_compat_normalizes_legacy_tuple_flags(): + """Assert that legacy tuple-style field flags are normalized for WTForms 3.""" + FieldListInputRequired.field_flags = ("required",) + Unique.field_flags = ("unique",) + + apply_wtforms_compat() + + assert FieldListInputRequired.field_flags == {"required": True} + assert Unique.field_flags == {"unique": True} + + +def test_apply_wtforms_compat_normalizes_select2_iter_choices(): + """Assert that Select2 fields yield WTForms 3-compatible choice tuples.""" + + class TestForm(Form): + status = Select2Field(choices=[("1", "Active")]) + + apply_wtforms_compat() + + form = TestForm() + + assert list(form.status.iter_choices()) == [("1", "Active", False, {})] + + +def test_apply_wtforms_compat_normalizes_query_select_iter_choices(): + """Assert that query-backed select fields yield WTForms 3-compatible choice tuples.""" + + class Choice: + def __init__(self, identifier, label): + self.identifier = identifier + self.label = label + + class TestForm(Form): + role = QuerySelectField( + query_factory=lambda: [Choice(1, "CSR")], + get_pk=lambda obj: obj.identifier, + get_label=lambda obj: obj.label, + ) + + apply_wtforms_compat() + + form = TestForm() + + assert list(form.role.iter_choices()) == [("1", "CSR", False, {})] + + +def test_apply_wtforms_compat_allows_legacy_select_widgets_to_render(): + """Assert that legacy select widgets still render after the compatibility patch is applied.""" + + class LegacySelectField(SelectFieldBase): + widget = Select2Field.widget + + def iter_choices(self): + yield ("1", "Active", False) + + class TestForm(Form): + status = LegacySelectField() + + apply_wtforms_compat() + + form = TestForm() + + assert 'option value="1"' in form.status() diff --git a/api/app/tests/test_flask3_smoke.py b/api/app/tests/test_flask3_smoke.py deleted file mode 100644 index 3ed9f8bf0..000000000 --- a/api/app/tests/test_flask3_smoke.py +++ /dev/null @@ -1,77 +0,0 @@ -import inspect -from types import SimpleNamespace - - -def _unwrap(func): - return inspect.unwrap(func) - - -def test_application_url_map_contains_admin_and_healthz(app): - routes = {rule.rule for rule in app.url_map.iter_rules()} - - assert "/admin/" in routes - assert "/api/v1/healthz/" in routes - - -def test_admin_index_renders_without_bootstrap3_assets(client): - response = client.get("/admin/") - - assert response.status_code == 200 - - body = response.get_data(as_text=True) - - assert "Admin Console" in body - assert "bootstrap3" not in body - - -def test_login_resource_redirects_authenticated_users_to_admin(app, monkeypatch): - from app.resources.theq import login as login_module - - fake_csr = SimpleNamespace(username="tester") - logged_in = [] - - monkeypatch.setattr(login_module, "get_username", lambda: "tester@idir") - monkeypatch.setattr(login_module.CSR, "find_by_username", lambda username: fake_csr) - monkeypatch.setattr(login_module, "login_user", lambda user: logged_in.append(user)) - - handler = _unwrap(login_module.Login.get) - - with app.test_request_context("/api/v1/login/"): - response = handler(login_module.Login()) - - assert logged_in == [fake_csr] - assert response.status_code == 302 - assert response.location.endswith("/admin/") - - -def test_join_room_handler_emits_success_for_authenticated_csr(app, monkeypatch): - del app - - from app.resources.theq import websocket - - joined_rooms = [] - emitted_events = [] - fake_csr = SimpleNamespace( - username="tester", - office=SimpleNamespace(office_name="Victoria"), - ) - - monkeypatch.setattr(websocket, "get_username", lambda: "tester@idir") - monkeypatch.setattr(websocket.CSR, "find_by_username", lambda username: fake_csr) - monkeypatch.setattr(websocket, "join_room", lambda room: joined_rooms.append(room)) - monkeypatch.setattr( - websocket, - "emit", - lambda event, payload=None: emitted_events.append((event, payload)), - ) - monkeypatch.setattr(websocket, "request", SimpleNamespace(sid="socket-1")) - - handler = _unwrap(websocket.on_join) - handler({}) - - assert joined_rooms == ["Victoria"] - assert emitted_events == [ - ("joinRoomSuccess", {"sucess": True}), - ("get_Csr_State_IDs", {"success": True}), - ("update_customer_list", {"success": True}), - ] diff --git a/api/app/tests/test_flask_admin_wtforms_compat.py b/api/app/tests/test_flask_admin_wtforms_compat.py deleted file mode 100644 index ad0896301..000000000 --- a/api/app/tests/test_flask_admin_wtforms_compat.py +++ /dev/null @@ -1,66 +0,0 @@ -from flask_admin.contrib.sqla.fields import QuerySelectField -from flask_admin.contrib.sqla.validators import Unique -from flask_admin.form.fields import Select2Field -from flask_admin.form.validators import FieldListInputRequired -from wtforms import Form -from wtforms.fields import SelectFieldBase - -from app.utilities.flask_admin_compat import apply_wtforms_compat - - -def test_apply_wtforms_compat_normalizes_legacy_tuple_flags(): - FieldListInputRequired.field_flags = ("required",) - Unique.field_flags = ("unique",) - - apply_wtforms_compat() - - assert FieldListInputRequired.field_flags == {"required": True} - assert Unique.field_flags == {"unique": True} - - -def test_apply_wtforms_compat_normalizes_select2_iter_choices(): - class TestForm(Form): - status = Select2Field(choices=[("1", "Active")]) - - apply_wtforms_compat() - - form = TestForm() - - assert list(form.status.iter_choices()) == [("1", "Active", False, {})] - - -def test_apply_wtforms_compat_normalizes_query_select_iter_choices(): - class Choice: - def __init__(self, identifier, label): - self.identifier = identifier - self.label = label - - class TestForm(Form): - role = QuerySelectField( - query_factory=lambda: [Choice(1, "CSR")], - get_pk=lambda obj: obj.identifier, - get_label=lambda obj: obj.label, - ) - - apply_wtforms_compat() - - form = TestForm() - - assert list(form.role.iter_choices()) == [("1", "CSR", False, {})] - - -def test_apply_wtforms_compat_allows_legacy_select_widgets_to_render(): - class LegacySelectField(SelectFieldBase): - widget = Select2Field.widget - - def iter_choices(self): - yield ("1", "Active", False) - - class TestForm(Form): - status = LegacySelectField() - - apply_wtforms_compat() - - form = TestForm() - - assert 'option value="1"' in form.status() diff --git a/api/app/tests/test_notification.py b/api/app/tests/test_notification.py new file mode 100644 index 000000000..6a55b3c3d --- /dev/null +++ b/api/app/tests/test_notification.py @@ -0,0 +1,40 @@ +import pytest +from app.utilities.notification_email import send_email +from flask import Flask + +pytestmark = pytest.mark.smoke + + +def test_send_email_posts_json_payload_with_timeout(monkeypatch): + """Assert that notification emails are sent as JSON with the expected auth header and timeout.""" + app = Flask(__name__) + app.config["NOTIFICATIONS_EMAIL_ENDPOINT"] = "https://example.com/email" + + recorded = {} + + class Response: + def raise_for_status(self): + return None + + def fake_post(url, headers=None, json=None, timeout=None): + recorded["url"] = url + recorded["headers"] = headers + recorded["json"] = json + recorded["timeout"] = timeout + return Response() + + monkeypatch.setattr("app.utilities.notification_email.requests.post", fake_post) + + with app.app_context(): + send_email( + "token-123", + "Subject", + "citizen@example.com", + "noreply@example.com", + "

Hello

", + ) + + assert recorded["url"] == "https://example.com/email" + assert recorded["headers"]["Authorization"] == "Bearer token-123" + assert recorded["json"]["subject"] == "Subject" + assert recorded["timeout"] == 30 diff --git a/api/app/tests/test_queue_flows.py b/api/app/tests/test_queue_flows.py deleted file mode 100644 index 31ad7bfed..000000000 --- a/api/app/tests/test_queue_flows.py +++ /dev/null @@ -1,301 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -import pytest - -from app.tests.api_test_support import assert_status, json_of - - -pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] - - -def _create_citizen(api_client, position: int, *, name: str, comments: Optional[str] = None): - payload = {"citizen_name": name} - if comments is not None: - payload["citizen_comments"] = comments - - response = api_client.post( - f"/citizens/{position}/add_citizen/", - json=payload, - ) - assert_status(response, 201) - return json_of(response)["citizen"] - - -def _update_citizen(api_client, citizen_id: int, **payload): - response = api_client.put(f"/citizens/{citizen_id}/", json=payload) - assert_status(response, 200) - return json_of(response)["citizen"] - - -def _create_service_request(api_client, citizen_id: int, *, service_id: int, channel_id: int, quantity: int): - response = api_client.post( - "/service_requests/", - json={ - "service_request": { - "citizen_id": citizen_id, - "service_id": service_id, - "channel_id": channel_id, - "quantity": quantity, - } - }, - ) - assert_status(response, 201) - return json_of(response)["service_request"] - - -def _citizen_detail(api_client, citizen_id: int): - response = api_client.get(f"/citizens/{citizen_id}/") - assert_status(response, 200) - return json_of(response)["citizen"] - - -def _queue_ids(api_client) -> list[int]: - response = api_client.get("/citizens/") - assert_status(response, 200) - return [citizen["citizen_id"] for citizen in json_of(response)["citizens"]] - - -def test_qt1_specific_invite_additional_service_and_reactivate(internal_ga_client, seeded_data, app): - citizen = _create_citizen(internal_ga_client, 0, name="QT1 Citizen", comments="Needs property tax") - updated = _update_citizen( - internal_ga_client, - citizen["citizen_id"], - citizen_name="QT1 Citizen", - citizen_comments="Needs property tax", - qt_xn_citizen_ind=0, - counter_id=seeded_data["counter_ids"]["counter"], - ) - - first_service = _create_service_request( - internal_ga_client, - updated["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=3, - ) - assert first_service["quantity"] == 3 - - add_to_queue_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/add_to_queue/") - specific_invite_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/invite/") - begin_service_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/begin_service/") - - assert_status(add_to_queue_response, 200) - assert_status(specific_invite_response, 200) - assert_status(begin_service_response, 200) - - second_service = _create_service_request( - internal_ga_client, - updated["citizen_id"], - service_id=seeded_data["service_ids"]["msp"], - channel_id=seeded_data["channel_ids"]["email"], - quantity=1, - ) - reactivate_response = internal_ga_client.post(f"/service_requests/{first_service['sr_id']}/activate/") - finish_response = internal_ga_client.post(f"/citizens/{updated['citizen_id']}/finish_service/") - - assert second_service["service_id"] == seeded_data["service_ids"]["msp"] - assert_status(reactivate_response, 200) - assert_status(finish_response, 200) - assert _queue_ids(internal_ga_client) == [] - - with app.app_context(): - from app.models.theq import Citizen, ServiceReq - - citizen_model = Citizen.query.filter_by(citizen_id=updated["citizen_id"]).first() - sr_models = ServiceReq.query.filter_by(citizen_id=updated["citizen_id"]).order_by(ServiceReq.sr_number).all() - assert citizen_model.cs.cs_state_name == "Received Services" - assert [service_request.sr_state.sr_code for service_request in sr_models] == ["Complete", "Complete"] - - -def test_qt2_begin_hold_resume_finish_flow(internal_ga_client, seeded_data): - citizen = _create_citizen(internal_ga_client, 0, name="QT2 Citizen") - _update_citizen( - internal_ga_client, - citizen["citizen_id"], - citizen_name="QT2 Citizen", - counter_id=seeded_data["counter_ids"]["counter"], - ) - _create_service_request( - internal_ga_client, - citizen["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=2, - ) - - assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/"), 200) - hold_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/place_on_hold/") - service_requests_response = internal_ga_client.get(f"/citizens/{citizen['citizen_id']}/service_requests/") - resume_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/") - finish_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") - - assert_status(hold_response, 200) - assert_status(service_requests_response, 200) - assert_status(resume_response, 200) - assert_status(finish_response, 200) - assert _queue_ids(internal_ga_client) == [] - - -def test_qt3_citizen_leaves_after_create(internal_ga_client, app): - citizen = _create_citizen(internal_ga_client, 0, name="QT3 Citizen") - leave_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/citizen_left/") - - assert_status(leave_response, 200) - - with app.app_context(): - from app.models.theq import Citizen - - citizen_model = Citizen.query.filter_by(citizen_id=citizen["citizen_id"]).first() - assert citizen_model.cs.cs_state_name == "Left before receiving services" - - -def test_qt4_citizen_leaves_after_waiting(internal_ga_client, seeded_data, app): - citizen = _create_citizen(internal_ga_client, 0, name="QT4 Citizen") - _create_service_request( - internal_ga_client, - citizen["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=1, - ) - assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/add_to_queue/"), 200) - leave_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/citizen_left/") - - assert_status(leave_response, 200) - - with app.app_context(): - from app.models.theq import Citizen - - citizen_model = Citizen.query.filter_by(citizen_id=citizen["citizen_id"]).first() - assert citizen_model.cs.cs_state_name == "Left before receiving services" - - -def test_qt5_update_service_request_quantity_and_service(internal_ga_client, seeded_data, app): - citizen = _create_citizen(internal_ga_client, 0, name="QT5 Citizen") - service_request = _create_service_request( - internal_ga_client, - citizen["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=3, - ) - assert_status(internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/begin_service/"), 200) - - quantity_update = internal_ga_client.put( - f"/service_requests/{service_request['sr_id']}/", - json={"quantity": 5}, - ) - service_update = internal_ga_client.put( - f"/service_requests/{service_request['sr_id']}/", - json={"service_id": seeded_data["service_ids"]["msp"]}, - ) - finish_response = internal_ga_client.post(f"/citizens/{citizen['citizen_id']}/finish_service/") - - assert_status(quantity_update, 200) - assert_status(service_update, 200) - assert_status(finish_response, 200) - - with app.app_context(): - from app.models.theq import ServiceReq - - service_request_model = ServiceReq.query.filter_by(sr_id=service_request["sr_id"]).first() - assert service_request_model.quantity == 5 - assert service_request_model.service_id == seeded_data["service_ids"]["msp"] - - -def test_qt6_generic_invite_prefers_quick_trans_counter(internal_ga_client, seeded_data): - first_citizen = _create_citizen(internal_ga_client, 0, name="QT6 First") - _update_citizen( - internal_ga_client, - first_citizen["citizen_id"], - citizen_name="QT6 First", - qt_xn_citizen_ind=0, - counter_id=seeded_data["counter_ids"]["counter"], - ) - _create_service_request( - internal_ga_client, - first_citizen["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=1, - ) - assert_status(internal_ga_client.post(f"/citizens/{first_citizen['citizen_id']}/add_to_queue/"), 200) - - second_citizen = _create_citizen(internal_ga_client, 1, name="QT6 Second") - _update_citizen( - internal_ga_client, - second_citizen["citizen_id"], - citizen_name="QT6 Second", - qt_xn_citizen_ind=1, - counter_id=seeded_data["counter_ids"]["quick_trans"], - ) - _create_service_request( - internal_ga_client, - second_citizen["citizen_id"], - service_id=seeded_data["service_ids"]["msp"], - channel_id=seeded_data["channel_ids"]["email"], - quantity=1, - ) - assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/add_to_queue/"), 200) - - first_invite = internal_ga_client.post("/citizens/invite/", json={}) - assert_status(first_invite, 200) - assert json_of(first_invite)["citizen"]["citizen_id"] == second_citizen["citizen_id"] - assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/begin_service/"), 200) - assert_status(internal_ga_client.post(f"/citizens/{second_citizen['citizen_id']}/finish_service/"), 200) - - second_invite = internal_ga_client.post("/citizens/invite/", json={}) - assert_status(second_invite, 200) - assert json_of(second_invite)["citizen"]["citizen_id"] == first_citizen["citizen_id"] - assert_status(internal_ga_client.post(f"/citizens/{first_citizen['citizen_id']}/citizen_left/"), 200) - assert _queue_ids(internal_ga_client) == [] - - -def test_qt7_generic_invite_prefers_standard_counter(internal_nonqtxn_client, seeded_data): - first_citizen = _create_citizen(internal_nonqtxn_client, 0, name="QT7 First") - _update_citizen( - internal_nonqtxn_client, - first_citizen["citizen_id"], - citizen_name="QT7 First", - qt_xn_citizen_ind=1, - counter_id=seeded_data["counter_ids"]["quick_trans"], - ) - _create_service_request( - internal_nonqtxn_client, - first_citizen["citizen_id"], - service_id=seeded_data["service_ids"]["msp"], - channel_id=seeded_data["channel_ids"]["email"], - quantity=1, - ) - assert_status(internal_nonqtxn_client.post(f"/citizens/{first_citizen['citizen_id']}/add_to_queue/"), 200) - - second_citizen = _create_citizen(internal_nonqtxn_client, 1, name="QT7 Second") - _update_citizen( - internal_nonqtxn_client, - second_citizen["citizen_id"], - citizen_name="QT7 Second", - qt_xn_citizen_ind=0, - counter_id=seeded_data["counter_ids"]["counter"], - ) - _create_service_request( - internal_nonqtxn_client, - second_citizen["citizen_id"], - service_id=seeded_data["service_ids"]["ptax"], - channel_id=seeded_data["channel_ids"]["phone"], - quantity=1, - ) - assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/add_to_queue/"), 200) - - first_invite = internal_nonqtxn_client.post("/citizens/invite/", json={}) - assert_status(first_invite, 200) - assert json_of(first_invite)["citizen"]["citizen_id"] == second_citizen["citizen_id"] - assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/begin_service/"), 200) - assert_status(internal_nonqtxn_client.post(f"/citizens/{second_citizen['citizen_id']}/finish_service/"), 200) - - second_invite = internal_nonqtxn_client.post("/citizens/invite/", json={}) - assert_status(second_invite, 200) - assert json_of(second_invite)["citizen"]["citizen_id"] == first_citizen["citizen_id"] - assert_status(internal_nonqtxn_client.post(f"/citizens/{first_citizen['citizen_id']}/citizen_left/"), 200) - assert _queue_ids(internal_nonqtxn_client) == [] diff --git a/api/app/tests/test_schema_compatibility.py b/api/app/tests/test_schema_compatibility.py index acb4c90b4..15e27f175 100644 --- a/api/app/tests/test_schema_compatibility.py +++ b/api/app/tests/test_schema_compatibility.py @@ -3,6 +3,8 @@ import pkgutil from datetime import datetime, timezone +import pytest + def _schema_classes(): import app.schemas as schema_package @@ -26,6 +28,7 @@ def _schema_classes(): def test_all_schema_classes_instantiate(app, seeded_database): + """Assert that every Marshmallow schema class still instantiates with fields.""" del seeded_database with app.app_context(): @@ -38,6 +41,7 @@ def test_all_schema_classes_instantiate(app, seeded_database): def test_schema_validate_is_safe_for_orm_objects(app, seeded_data): + """Assert that schema validation still accepts ORM instances under Marshmallow 4.""" with app.app_context(): from app.models.theq import Citizen, Service from app.schemas.theq import CitizenSchema, ServiceSchema @@ -60,6 +64,7 @@ def test_schema_validate_is_safe_for_orm_objects(app, seeded_data): def test_service_schema_serializes_parent_name(app, seeded_data): + """Assert that services still serialize parent names and DLKT flags correctly.""" del seeded_data with app.app_context(): @@ -95,6 +100,7 @@ def test_service_schema_serializes_parent_name(app, seeded_data): def test_exam_schema_preserves_exam_received_date_format(app, seeded_data): + """Assert that exam received dates keep their UTC contract format during serialization.""" with app.app_context(): from app.models.bookings import Exam, ExamType, Invigilator from app.models.theq import Office @@ -129,6 +135,7 @@ def test_exam_schema_preserves_exam_received_date_format(app, seeded_data): def test_booking_schema_post_dump_supports_single_and_many(app, seeded_data): + """Assert that booking post-dump hooks normalize invigilators for single and many dumps.""" with app.app_context(): from app.models.bookings import Booking, Invigilator, Room from app.models.theq import Office @@ -160,12 +167,15 @@ def test_booking_schema_post_dump_supports_single_and_many(app, seeded_data): dumped_single = schema.dump(booking) dumped_many = schema.dump([booking], many=True) - expected_invigilators = [invigilator.invigilator_id for invigilator in invigilators] + expected_invigilators = [ + invigilator.invigilator_id for invigilator in invigilators + ] assert dumped_single["invigilators"] == expected_invigilators assert dumped_many[0]["invigilators"] == expected_invigilators def test_csr_schema_post_dump_supports_single_and_many(app, seeded_data): + """Assert that CSR post-dump hooks preserve the counter alias for single and many dumps.""" del seeded_data with app.app_context(): @@ -180,10 +190,13 @@ def test_csr_schema_post_dump_supports_single_and_many(app, seeded_data): dumped_many = schema.dump(csrs, many=True) assert dumped_single["counter"] == dumped_single["counter_id"] - assert [csr["counter"] for csr in dumped_many] == [csr["counter_id"] for csr in dumped_many] + assert [csr["counter"] for csr in dumped_many] == [ + csr["counter_id"] for csr in dumped_many + ] def test_appointment_availability_schema_smoke(app, seeded_data): + """Assert that appointment availability schema dumps the core slot fields needed by the API.""" with app.app_context(): from app.models.bookings import Appointment from app.schemas.bookings.appointment_availability_schema import ( diff --git a/api/app/tests/test_sqlalchemy_smoke.py b/api/app/tests/test_sqlalchemy.py similarity index 91% rename from api/app/tests/test_sqlalchemy_smoke.py rename to api/app/tests/test_sqlalchemy.py index 31e1c1b49..4cdc0d820 100644 --- a/api/app/tests/test_sqlalchemy_smoke.py +++ b/api/app/tests/test_sqlalchemy.py @@ -1,9 +1,10 @@ from datetime import datetime, timedelta, timezone +import pytest from sqlalchemy import text - def test_app_boots_with_disposable_postgres(app, postgres_database): + """Assert that the Flask app boots against the disposable Postgres database.""" assert app.config["SQLALCHEMY_DATABASE_URI"] == postgres_database["database_uri"] with app.app_context(): @@ -11,16 +12,19 @@ def test_app_boots_with_disposable_postgres(app, postgres_database): def test_db_current_command_runs(cli_runner): + """Assert that the Alembic current command runs successfully in the test harness.""" result = cli_runner.invoke(args=["db", "current"]) assert result.exit_code == 0, result.output def test_db_upgrade_command_runs(migrated_database): + """Assert that database migrations upgrade cleanly in the smoke suite.""" assert migrated_database.exit_code == 0, migrated_database.output def test_healthz_uses_database_connection(client, migrated_database): + """Assert that the health endpoint confirms a working database connection.""" response = client.get("/api/v1/healthz/") assert response.status_code == 200 @@ -28,6 +32,7 @@ def test_healthz_uses_database_connection(client, migrated_database): def test_appointment_crud_and_version_rows(app, db, migrated_database): + """Assert that appointment CRUD still writes versioning and transaction rows.""" from app.models.bookings import Appointment from app.models.theq.office import Office from app.models.theq.smartboard import SmartBoard @@ -81,7 +86,10 @@ def test_appointment_crud_and_version_rows(app, db, migrated_database): assert transaction_count >= 2 -def test_local_utc_type_round_trips_booking_appointment_and_citizen(app, db, migrated_database): +def test_local_utc_type_round_trips_booking_appointment_and_citizen( + app, db, migrated_database +): + """Assert that LocalUTC-backed columns round-trip as UTC-aware datetimes.""" from app.models.bookings import Appointment, Booking, Room from app.models.theq.citizen import Citizen from app.models.theq.citizen_state import CitizenState diff --git a/api/app/tests/test_dependency_modernization.py b/api/app/tests/test_timezone_helpers.py similarity index 51% rename from api/app/tests/test_dependency_modernization.py rename to api/app/tests/test_timezone_helpers.py index 08ddb5e94..42f3be9fe 100644 --- a/api/app/tests/test_dependency_modernization.py +++ b/api/app/tests/test_timezone_helpers.py @@ -1,14 +1,15 @@ from datetime import datetime -from flask import Flask - +import pytest from app.utilities.date_util import add_delta_to_time, current_pacific_time -from app.utilities.notification_email import send_email from app.utilities.sqlalchemy_compat import utcnow from app.utilities.timezone_utils import as_utc, get_timezone, localize +pytestmark = pytest.mark.smoke + def test_localize_preserves_dst_offset_for_vancouver(): + """Assert that Vancouver localization keeps the pre-DST offset for naive wall times.""" localized = localize(datetime(2026, 3, 8, 1, 30), "America/Vancouver") assert localized.tzinfo == get_timezone("America/Vancouver") @@ -16,6 +17,7 @@ def test_localize_preserves_dst_offset_for_vancouver(): def test_as_utc_marks_naive_datetimes_as_utc(): + """Assert that naive datetimes are treated as UTC by the compatibility helper.""" converted = as_utc(datetime(2026, 3, 8, 9, 30)) assert converted.utcoffset().total_seconds() == 0 @@ -23,48 +25,25 @@ def test_as_utc_marks_naive_datetimes_as_utc(): def test_add_delta_to_time_returns_zoneinfo_backed_time(): - shifted = add_delta_to_time(datetime.strptime("08:30", "%H:%M").time(), "America/Vancouver", minutes=30) + """Assert that time arithmetic returns a timezone-aware Vancouver time object.""" + shifted = add_delta_to_time( + datetime.strptime("08:30", "%H:%M").time(), "America/Vancouver", minutes=30 + ) assert shifted.strftime("%H:%M") == "09:00" assert shifted.tzinfo == get_timezone("America/Vancouver") def test_current_pacific_time_uses_vancouver_zone(): + """Assert that the Pacific-time helper uses the Vancouver zone definition.""" pacific_now = current_pacific_time() assert pacific_now.tzinfo == get_timezone("America/Vancouver") def test_utcnow_returns_aware_utc_datetime(): + """Assert that the SQLAlchemy compatibility helper returns an aware UTC datetime.""" current = utcnow() assert current.utcoffset().total_seconds() == 0 assert current.tzname() == "UTC" - - -def test_send_email_posts_json_payload_with_timeout(monkeypatch): - app = Flask(__name__) - app.config["NOTIFICATIONS_EMAIL_ENDPOINT"] = "https://example.com/email" - - recorded = {} - - class Response: - def raise_for_status(self): - return None - - def fake_post(url, headers=None, json=None, timeout=None): - recorded["url"] = url - recorded["headers"] = headers - recorded["json"] = json - recorded["timeout"] = timeout - return Response() - - monkeypatch.setattr("app.utilities.notification_email.requests.post", fake_post) - - with app.app_context(): - send_email("token-123", "Subject", "citizen@example.com", "noreply@example.com", "

Hello

") - - assert recorded["url"] == "https://example.com/email" - assert recorded["headers"]["Authorization"] == "Bearer token-123" - assert recorded["json"]["subject"] == "Subject" - assert recorded["timeout"] == 30 diff --git a/api/app/tests/test_websocket_smoke.py b/api/app/tests/test_websocket_smoke.py new file mode 100644 index 000000000..7e10cce19 --- /dev/null +++ b/api/app/tests/test_websocket_smoke.py @@ -0,0 +1,194 @@ +import inspect +from types import SimpleNamespace + +import pytest + + +def _unwrap(func): + return inspect.unwrap(func) + + +def _import_websocket(monkeypatch): + class Response: + def read(self): + return ( + b'{"jwks_uri":"https://example.com/jwks.json","issuer":"https://example.com/"}' + ) + + monkeypatch.setattr( + "flask_jwt_oidc.jwt_manager.urlopen", + lambda url: Response(), + ) + + from app.resources.theq import websocket + + return websocket + + +pytestmark = pytest.mark.smoke + + +def test_join_room_handler_emits_success_for_authenticated_csr(monkeypatch): + """Assert that websocket room joins still emit the expected success events.""" + websocket = _import_websocket(monkeypatch) + + joined_rooms = [] + emitted_events = [] + fake_csr = SimpleNamespace( + username="tester", + office=SimpleNamespace(office_name="Victoria"), + ) + + monkeypatch.setattr(websocket, "get_username", lambda: "tester@idir") + monkeypatch.setattr(websocket.CSR, "find_by_username", lambda username: fake_csr) + monkeypatch.setattr(websocket, "join_room", lambda room: joined_rooms.append(room)) + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + monkeypatch.setattr(websocket, "request", SimpleNamespace(sid="socket-1")) + + handler = _unwrap(websocket.on_join) + handler({}) + + assert joined_rooms == ["Victoria"] + assert emitted_events == [ + ("joinRoomSuccess", {"sucess": True}), + ("get_Csr_State_IDs", {"success": True}), + ("update_customer_list", {"success": True}), + ] + + +def test_join_room_handler_fails_without_a_username(monkeypatch): + """Assert that joinRoom fails cleanly when no authenticated username is present.""" + websocket = _import_websocket(monkeypatch) + + emitted_events = [] + + monkeypatch.setattr(websocket, "get_username", lambda: "") + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + + handler = _unwrap(websocket.on_join) + handler({}) + + assert emitted_events == [("joinRoomFail", {"success": False})] + + +def test_join_room_handler_fails_when_csr_lookup_misses(monkeypatch): + """Assert that joinRoom fails when the username does not resolve to a CSR.""" + websocket = _import_websocket(monkeypatch) + + emitted_events = [] + + monkeypatch.setattr(websocket, "get_username", lambda: "missing@idir") + monkeypatch.setattr(websocket.CSR, "find_by_username", lambda username: None) + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + + handler = _unwrap(websocket.on_join) + handler({}) + + assert emitted_events == [("joinRoomFail", {"success": False})] + + +def test_join_smartboard_room_success_joins_the_expected_room(monkeypatch): + """Assert that smartboard room joins use the office-scoped socket room name.""" + websocket = _import_websocket(monkeypatch) + + joined_rooms = [] + emitted_events = [] + + monkeypatch.setattr(websocket, "join_room", lambda room: joined_rooms.append(room)) + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + monkeypatch.setattr(websocket, "request", SimpleNamespace(sid="socket-2")) + + websocket.on_join_smartboard({"office_id": "7"}) + + assert joined_rooms == ["sb-7"] + assert emitted_events == [("joinSmartboardRoomSuccess", None)] + + +def test_join_smartboard_room_rejects_missing_office_id(monkeypatch): + """Assert that smartboard joins fail with a clear message when office_id is missing.""" + websocket = _import_websocket(monkeypatch) + + emitted_events = [] + + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + + websocket.on_join_smartboard({}) + + assert emitted_events == [ + ( + "joinSmartboardRoomFail", + {"sucess": False, "message": "office_id must be passed to this method"}, + ) + ] + + +def test_join_smartboard_room_rejects_non_integer_office_id(monkeypatch): + """Assert that smartboard joins fail when office_id is not numeric.""" + websocket = _import_websocket(monkeypatch) + + emitted_events = [] + + monkeypatch.setattr( + websocket, + "emit", + lambda event, payload=None: emitted_events.append((event, payload)), + ) + + websocket.on_join_smartboard({"office_id": "abc"}) + + assert emitted_events == [ + ( + "joinSmartboardRoomFail", + {"sucess": False, "message": "office_id must be an integer"}, + ) + ] + + +def test_clear_csr_user_id_updates_the_csr_cache(monkeypatch): + """Assert that the clear-cache websocket event delegates to CSR.update_user_cache.""" + websocket = _import_websocket(monkeypatch) + + updated_ids = [] + + monkeypatch.setattr( + websocket.CSR, "update_user_cache", lambda csr_id: updated_ids.append(csr_id) + ) + + websocket.clear_csr_user_id(42) + + assert updated_ids == [42] + + +def test_sync_offices_cache_clears_the_office_cache(monkeypatch): + """Assert that office cache sync delegates to Office.clear_offices_cache.""" + websocket = _import_websocket(monkeypatch) + + calls = [] + + monkeypatch.setattr( + websocket.Office, "clear_offices_cache", lambda: calls.append("cleared") + ) + + websocket.sync_offices_cache() + + assert calls == ["cleared"] diff --git a/api/app/tests/validation/__init__.py b/api/app/tests/validation/__init__.py new file mode 100644 index 000000000..ae01f8262 --- /dev/null +++ b/api/app/tests/validation/__init__.py @@ -0,0 +1 @@ +"""Validation tests for explicit route-level API error handling.""" diff --git a/api/app/tests/validation/test_appointment_validation.py b/api/app/tests/validation/test_appointment_validation.py new file mode 100644 index 000000000..182b9a777 --- /dev/null +++ b/api/app/tests/validation/test_appointment_validation.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import pytest +from app.tests.api_test_support import ( + assert_json_response, + create_public_user, + json_of, + public_slot_payload, + slot_window_to_iso, +) + +pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] + + +def test_anonymous_draft_create_rejects_a_conflicting_slot(bare_client, seeded_data): + """Assert that anonymous draft creation returns CONFLICT_APPOINTMENT when the slot is already reserved.""" + payload, _day_key, _slots = public_slot_payload( + bare_client, seeded_data, minimum_slots=1 + ) + assert_json_response(bare_client.post("/appointments/draft", json=payload), 201) + + response = bare_client.post("/appointments/draft", json=payload) + + assert_json_response(response, 400) + assert json_of(response)["code"] == "CONFLICT_APPOINTMENT" + + +def test_public_appointment_create_rejects_a_conflict_across_different_users( + public_client, public_client_alt, seeded_data +): + """Assert that one public user's appointment blocks a different public user from taking the same slot.""" + create_public_user(public_client) + create_public_user(public_client_alt) + payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=1 + ) + assert_json_response(public_client.post("/appointments/", json=payload), 201) + + response = public_client_alt.post("/appointments/", json=payload) + + assert_json_response(response, 400) + assert json_of(response)["code"] == "CONFLICT_APPOINTMENT" + + +def test_public_appointment_update_rejects_moving_onto_another_users_slot( + public_client, public_client_alt, seeded_data +): + """Assert that updating a public appointment into another user's booked slot returns CONFLICT_APPOINTMENT.""" + create_public_user(public_client) + payload, day_key, slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + first_response = public_client.post("/appointments/", json=payload) + assert_json_response(first_response, 201) + taken_appointment = json_of(first_response)["appointment"] + + create_public_user(public_client_alt) + other_start_time, other_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + second_response = public_client_alt.post( + "/appointments/", + json={ + **payload, + "start_time": other_start_time, + "end_time": other_end_time, + }, + ) + assert_json_response(second_response, 201) + other_appointment = json_of(second_response)["appointment"] + + response = public_client_alt.put( + f"/appointments/{other_appointment['appointment_id']}/", + json={ + "comments": "Move into a conflicting slot", + "office_id": other_appointment["office_id"], + "service_id": other_appointment["service_id"], + "start_time": taken_appointment["start_time"], + "end_time": taken_appointment["end_time"], + }, + ) + + assert_json_response(response, 400) + assert json_of(response)["code"] == "CONFLICT_APPOINTMENT" + + +def test_public_user_cannot_update_another_users_appointment( + public_client, public_client_alt, seeded_data +): + """Assert that a public user receives 403 when attempting to update another public user's appointment.""" + create_public_user(public_client) + payload, day_key, slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + first_response = public_client.post("/appointments/", json=payload) + assert_json_response(first_response, 201) + appointment = json_of(first_response)["appointment"] + + create_public_user(public_client_alt) + alternate_start_time, alternate_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + response = public_client_alt.put( + f"/appointments/{appointment['appointment_id']}/", + json={ + "comments": "Attempted cross-user update", + "office_id": appointment["office_id"], + "service_id": appointment["service_id"], + "start_time": alternate_start_time, + "end_time": alternate_end_time, + }, + ) + + assert response.status_code == 403, response.get_data(as_text=True) + + +def test_public_user_create_rejects_when_the_daily_limit_is_reached( + public_client, seeded_data +): + """Assert that the public appointment create route freezes the daily booking limit error contract.""" + create_public_user(public_client) + payload, day_key, slots = public_slot_payload( + public_client, seeded_data, minimum_slots=2 + ) + first_response = public_client.post("/appointments/", json=payload) + assert_json_response(first_response, 201) + + alternate_start_time, alternate_end_time = slot_window_to_iso( + day_key, + slots[1], + seeded_data["office_timezones"]["limited_office"], + ) + response = public_client.post( + "/appointments/", + json={ + **payload, + "start_time": alternate_start_time, + "end_time": alternate_end_time, + }, + ) + + assert_json_response(response, 400) + assert json_of(response) == { + "code": "MAX_NO_OF_APPOINTMENTS_REACHED", + "message": "Maximum number of appointments reached", + } diff --git a/api/app/tests/validation/test_booking_validation.py b/api/app/tests/validation/test_booking_validation.py new file mode 100644 index 000000000..3242b56d9 --- /dev/null +++ b/api/app/tests/validation/test_booking_validation.py @@ -0,0 +1,37 @@ +import pytest +from app.tests.api_test_support import assert_json_response, future_utc_window, json_of + +pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] + + +def test_booking_create_rejects_an_empty_payload(internal_ga_client): + """Assert that booking creation rejects an empty JSON body with the stable 400 response.""" + response = internal_ga_client.post("/bookings/", json={}) + + assert_json_response(response, 400) + assert json_of(response)["message"] == "No input data received for creating a booking" + + +def test_booking_create_rejects_a_booking_for_a_different_office( + internal_ga_client, seeded_data +): + """Assert that non-designate CSRs cannot create bookings for a different office.""" + start_time, end_time = future_utc_window(2, duration_minutes=120) + response = internal_ga_client.post( + "/bookings/", + json={ + "booking_name": "Wrong office booking", + "booking_contact_information": "booking@example.com", + "fees": "false", + "office_id": seeded_data["office_ids"]["limited_office"], + "room_id": seeded_data["room_id"], + "start_time": start_time, + "end_time": end_time, + "invigilator_id": [seeded_data["invigilator_ids"][0]], + }, + ) + + assert_json_response(response, 403) + assert json_of(response)["message"] == ( + "The Booking Office ID and CSR Office ID do not match!" + ) diff --git a/api/app/tests/validation/test_csr_validation.py b/api/app/tests/validation/test_csr_validation.py new file mode 100644 index 000000000..da46c8c59 --- /dev/null +++ b/api/app/tests/validation/test_csr_validation.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import pytest +from app.tests.api_test_support import json_of + +pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] + + +def test_csr_update_rejects_an_empty_json_payload(internal_ga_client, seeded_data): + """Assert that the CSR update endpoint returns 400 when no editable fields are provided.""" + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={}, + ) + + assert response.status_code == 400, response.get_data(as_text=True) + assert json_of(response)["message"] == "No input data received for updating CSR" + + +def test_csr_update_rejects_editing_a_different_csr( + internal_ga_client, seeded_data, app +): + """Assert that a CSR cannot edit another CSR's record.""" + with app.app_context(): + from app.models.theq import CSR + + original = CSR.query.filter_by( + csr_id=seeded_data["csr_ids"]["non_qtxn"] + ).first() + assert original is not None + original_receptionist_ind = original.receptionist_ind + + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['non_qtxn']}/", + json={"receptionist_ind": 0}, + ) + + assert response.status_code == 403, response.get_data(as_text=True) + assert json_of(response)["message"] == "You do not have permission to edit this CSR" + + with app.app_context(): + from app.models.theq import CSR + + persisted = CSR.query.filter_by( + csr_id=seeded_data["csr_ids"]["non_qtxn"] + ).first() + assert persisted is not None + assert persisted.receptionist_ind == original_receptionist_ind + + +def test_csr_update_rejects_an_invalid_csr_state_type(internal_ga_client, seeded_data): + """Assert that Marshmallow validation returns 422 when csr_state_id is not an integer.""" + response = internal_ga_client.put( + f"/csrs/{seeded_data['csr_ids']['ga']}/", + json={"csr_state_id": "not-an-integer"}, + ) + + assert response.status_code == 422, response.get_data(as_text=True) + assert "csr_state_id" in json_of(response)["message"] diff --git a/api/app/tests/validation/test_exam_validation.py b/api/app/tests/validation/test_exam_validation.py new file mode 100644 index 000000000..34d00038c --- /dev/null +++ b/api/app/tests/validation/test_exam_validation.py @@ -0,0 +1,47 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of +from app.tests.auth.auth_support import create_internal_exam_bundle, patch_exam_integrations + +pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] + + +def test_exam_download_reports_when_the_package_is_not_ready( + internal_ga_client, monkeypatch, seeded_data +): + """Assert that exam downloads freeze the pending-package error contract.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + patch_exam_integrations( + monkeypatch, + download_job={"jobStatus": "QUEUED", "jobProperties": {}}, + ) + + response = internal_ga_client.get(f"/exams/{exam['exam_id']}/download/") + + assert_json_response(response, 400) + assert json_of(response) == { + "message": "Package not yet generated", + "status": "QUEUED", + } + + +def test_exam_email_invigilator_requires_name_email_and_phone( + internal_ga_client, monkeypatch, seeded_data +): + """Assert that emailing an invigilator rejects incomplete contact details.""" + _booking, exam = create_internal_exam_bundle(internal_ga_client, seeded_data) + patch_exam_integrations(monkeypatch, email_result=True) + + response = internal_ga_client.post( + f"/exams/{exam['exam_id']}/email_invigilator/", + json={ + "invigilator_id": seeded_data["invigilator_ids"][0], + "invigilator_name": "Homer Simpson", + "invigilator_email": "homer@example.com", + "invigilator_phone": "", + }, + ) + + assert_json_response(response, 422) + assert json_of(response)["message"] == ( + "Invigilator name, email, and phone number are required" + ) diff --git a/api/app/tests/validation/test_service_refresh_validation.py b/api/app/tests/validation/test_service_refresh_validation.py new file mode 100644 index 000000000..a6b469a47 --- /dev/null +++ b/api/app/tests/validation/test_service_refresh_validation.py @@ -0,0 +1,20 @@ +import pytest +from app.tests.api_test_support import assert_json_response, json_of + +pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] + + +def test_service_refresh_requires_an_office_id(internal_ga_client): + """Assert that refresh requests without office_id fail with the stable error payload.""" + response = internal_ga_client.get("/services/refresh/") + + assert_json_response(response, 400) + assert json_of(response)["message"] == "no office specified" + + +def test_service_refresh_rejects_non_integer_office_ids(internal_ga_client): + """Assert that refresh requests reject non-numeric office ids with a stable validation message.""" + response = internal_ga_client.get("/services/refresh/?office_id=abc") + + assert_json_response(response, 400) + assert json_of(response)["message"] == "office_id must be an integer." diff --git a/api/scripts/run_sqlalchemy_smoke_tests.sh b/api/scripts/run_api_full_tests.sh similarity index 75% rename from api/scripts/run_sqlalchemy_smoke_tests.sh rename to api/scripts/run_api_full_tests.sh index edf634183..d97e3ba6e 100755 --- a/api/scripts/run_sqlalchemy_smoke_tests.sh +++ b/api/scripts/run_api_full_tests.sh @@ -5,4 +5,4 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" api_dir="$(cd "${script_dir}/.." && pwd)" cd "${api_dir}" -uv run pytest app/tests -q +uv run pytest app/tests -q --require-integration-db diff --git a/api/scripts/run_sqlalchemy_warn20_tests.sh b/api/scripts/run_api_integration_tests.sh similarity index 50% rename from api/scripts/run_sqlalchemy_warn20_tests.sh rename to api/scripts/run_api_integration_tests.sh index d4bac87fd..aa4079eae 100755 --- a/api/scripts/run_sqlalchemy_warn20_tests.sh +++ b/api/scripts/run_api_integration_tests.sh @@ -5,7 +5,6 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" api_dir="$(cd "${script_dir}/.." && pwd)" cd "${api_dir}" -export SQLALCHEMY_WARN_20=1 -uv run pytest app/tests -q \ - -W "error::sqlalchemy.exc.RemovedIn20Warning" \ - -W "error::sqlalchemy.exc.SADeprecationWarning" +uv run pytest app/tests -m integration -q \ + --override-ini "addopts=--strict-markers" \ + --require-integration-db diff --git a/api/scripts/run_api_smoke_tests.sh b/api/scripts/run_api_smoke_tests.sh new file mode 100755 index 000000000..4895648bf --- /dev/null +++ b/api/scripts/run_api_smoke_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +api_dir="$(cd "${script_dir}/.." && pwd)" + +cd "${api_dir}" +uv run pytest app/tests -m smoke -q --override-ini "addopts=--strict-markers" diff --git a/api/setup.cfg b/api/setup.cfg index 2e38aa834..652123ea7 100755 --- a/api/setup.cfg +++ b/api/setup.cfg @@ -44,11 +44,15 @@ lines_after_imports = 2 [tool:pytest] -addopts = --cov=app --cov-report html:htmlcov --cov-report xml:coverage.xml +addopts = --strict-markers --cov=app --cov-branch --cov-report html:htmlcov --cov-report xml:coverage.xml testpaths = app/tests python_files = test*.py markers = + integration: Disposable-Postgres integration coverage that requires the seeded API harness. contracts: Stable API contract and smoke assertions for seeded local test data. flows: Stateful end-to-end API regression workflows translated from the legacy Postman collections. + auth: Authorization boundary tests for seeded synthetic identities. + validation: Input validation and error handling assertions. + smoke: Infrastructure and compatibility smoke assertions. filterwarnings = ignore::UserWarning From 20101813c754db5d2c342404270f6ea14c88b8eb Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 1 Apr 2026 13:06:55 -0700 Subject: [PATCH 32/81] Update Dockerfile --- api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/Dockerfile b/api/Dockerfile index 516c2c7ce..1265d0465 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -22,4 +22,4 @@ COPY . . EXPOSE 8080 -CMD ["gunicorn", "wsgi", "--bind", "0.0.0.0:8080", "--access-logfile=-", "--config=gunicorn_config.py"] +CMD ["gunicorn", "wsgi", "--bind", "0.0.0.0:8080", "--access-logfile=-", "--no-control-socket", "--config=gunicorn_config.py"] From 5bae24742bd5a2e1da8d402c18ff9f1412795965 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 1 Apr 2026 14:53:31 -0700 Subject: [PATCH 33/81] Fix DB URI encoding for special-character passwords Changes `config.py` to build SQLAlchemy database connection strings with `URL.create(...)` instead of raw string interpolation, which permits the use of `@` and other reserved characters in database passwords under recent SQLAlchemy versions. Adds a regression test covering a password that contains `@`. --- api/app/tests/test_config.py | 45 ++++++++++++++++++++++++++++++++++++ api/config.py | 22 ++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 api/app/tests/test_config.py diff --git a/api/app/tests/test_config.py b/api/app/tests/test_config.py new file mode 100644 index 000000000..3332af1da --- /dev/null +++ b/api/app/tests/test_config.py @@ -0,0 +1,45 @@ +import importlib + +import pytest +from sqlalchemy.engine import make_url + +pytestmark = [pytest.mark.smoke] + + +CONFIG_ENV_VARS = ( + "DATABASE_ENGINE", + "DATABASE_USERNAME", + "DATABASE_PASSWORD", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_NAME", +) + + +def _reload_config_module(): + config_module = importlib.import_module("config") + return importlib.reload(config_module) + + +def test_development_config_encodes_database_password_special_characters(): + with pytest.MonkeyPatch.context() as monkeypatch: + for key in CONFIG_ENV_VARS: + monkeypatch.delenv(key, raising=False) + + monkeypatch.setenv("DATABASE_ENGINE", "postgres") + monkeypatch.setenv("DATABASE_USERNAME", "queue_user") + monkeypatch.setenv("DATABASE_PASSWORD", "abc@123") + monkeypatch.setenv("DATABASE_HOST", "db.internal") + monkeypatch.setenv("DATABASE_PORT", "5432") + monkeypatch.setenv("DATABASE_NAME", "queue_db") + + config_module = _reload_config_module() + uri = config_module.DevelopmentConfig.SQLALCHEMY_DATABASE_URI + parsed = make_url(uri) + + assert "%40" in uri + assert parsed.password == "abc@123" + assert parsed.host == "db.internal" + assert parsed.drivername == "postgresql+psycopg2" + + _reload_config_module() diff --git a/api/config.py b/api/config.py index 71018e95a..aee3ac5e1 100644 --- a/api/config.py +++ b/api/config.py @@ -2,6 +2,7 @@ import os import dotenv from pprint import pprint +from sqlalchemy.engine import URL # Load all the environment variables from a .env file located in some directory above. dotenv.load_dotenv(dotenv.find_dotenv()) @@ -23,6 +24,23 @@ def normalize_database_engine(db_engine): return db_engine +def normalize_database_port(db_port): + if db_port in (None, ""): + return None + return int(db_port) + + +def build_database_uri(engine, user, password, host, port, name): + return URL.create( + drivername=engine, + username=user, + password=password, + host=host, + port=normalize_database_port(port), + database=name, + ).render_as_string(hide_password=False) + + class BaseConfig(object): # Set up miscellaneous environment variables. @@ -76,7 +94,7 @@ class BaseConfig(object): DB_POOL_TIMEOUT = os.getenv('DATABASE_TIMEOUT_STRING', '') DB_CONNECT_TIMEOUT = os.getenv('DATABASE_CONNECT_TIMEOUT_STRING', '') - SQLALCHEMY_DATABASE_URI = '{engine}://{user}:{password}@{host}:{port}/{name}'.format( + SQLALCHEMY_DATABASE_URI = build_database_uri( engine=DB_ENGINE, user=DB_USER, password=DB_PASSWORD, @@ -211,7 +229,7 @@ class LocalConfig(BaseConfig): DB_NAME = os.getenv('DATABASE_NAME', 'qsystem') DB_HOST = os.getenv('DATABASE_HOST', '127.0.0.1') DB_PORT = os.getenv('DATABASE_PORT', '5432') - SQLALCHEMY_DATABASE_URI = '{engine}://{user}:{password}@{host}:{port}/{name}'.format( + SQLALCHEMY_DATABASE_URI = build_database_uri( engine=DB_ENGINE, user=DB_USER, password=DB_PASSWORD, From 506b7b99e49cc7ef6eb0a0f1886cf3d4c8b30c97 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Wed, 1 Apr 2026 16:03:47 -0700 Subject: [PATCH 34/81] Migrate python-jose to cryptography backend --- api/Dockerfile | 3 ++- api/pyproject.toml | 2 +- api/requirements.txt | 4 +++- api/requirements_dev.txt | 4 +++- api/uv.lock | 9 +++++++-- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 1265d0465..65fdc8786 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -16,7 +16,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY requirements.txt . RUN pip install --upgrade pip setuptools wheel && \ - pip install -r requirements.txt + pip install -r requirements.txt && \ + pip uninstall -y rsa ecdsa pyasn1 COPY . . diff --git a/api/pyproject.toml b/api/pyproject.toml index a2de27e1b..d50c5c66b 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ "psycopg2-binary>=2.9,<2.10", "python-dateutil>=2.9,<2.10", "python-dotenv>=1.2,<1.3", - "python-jose>=3.5,<3.6", + "python-jose[cryptography]>=3.5,<3.6", "redis>=7.4,<7.5", "requests>=2.33,<2.34", "snowplow-tracker>=1.1,<1.2", diff --git a/api/requirements.txt b/api/requirements.txt index 01a9961fd..6664325fb 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -258,7 +258,9 @@ cryptography==46.0.6 \ --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 - # via flask-jwt-oidc + # via + # flask-jwt-oidc + # python-jose dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa diff --git a/api/requirements_dev.txt b/api/requirements_dev.txt index d946c3943..8881f4d73 100644 --- a/api/requirements_dev.txt +++ b/api/requirements_dev.txt @@ -258,7 +258,9 @@ cryptography==46.0.6 \ --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 - # via flask-jwt-oidc + # via + # flask-jwt-oidc + # python-jose dill==0.4.1 \ --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa diff --git a/api/uv.lock b/api/uv.lock index be59f17fa..13a55b1aa 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1017,6 +1017,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, ] +[package.optional-dependencies] +cryptography = [ + { name = "cryptography" }, +] + [[package]] name = "python-socketio" version = "5.16.1" @@ -1062,7 +1067,7 @@ dependencies = [ { name = "psycopg2-binary" }, { name = "python-dateutil" }, { name = "python-dotenv" }, - { name = "python-jose" }, + { name = "python-jose", extra = ["cryptography"] }, { name = "redis" }, { name = "requests" }, { name = "snowplow-tracker" }, @@ -1108,7 +1113,7 @@ requires-dist = [ { name = "psycopg2-binary", specifier = ">=2.9,<2.10" }, { name = "python-dateutil", specifier = ">=2.9,<2.10" }, { name = "python-dotenv", specifier = ">=1.2,<1.3" }, - { name = "python-jose", specifier = ">=3.5,<3.6" }, + { name = "python-jose", extras = ["cryptography"], specifier = ">=3.5,<3.6" }, { name = "redis", specifier = ">=7.4,<7.5" }, { name = "requests", specifier = ">=2.33,<2.34" }, { name = "snowplow-tracker", specifier = ">=1.1,<1.2" }, From af0548b0a8b4420beb2e4c4558d7ec9a31a1a843 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Tue, 7 Apr 2026 11:08:31 -0700 Subject: [PATCH 35/81] Fix unnecessary alembic migration --- api/app/models/bookings/exam.py | 7 +- api/app/models/theq/public_user.py | 2 +- api/app/models/theq/service.py | 4 +- api/migrations/versions/c388edd5dc5a_.py | 207 ----------------------- 4 files changed, 7 insertions(+), 213 deletions(-) delete mode 100644 api/migrations/versions/c388edd5dc5a_.py diff --git a/api/app/models/bookings/exam.py b/api/app/models/bookings/exam.py index bdfda45a7..9376e9f05 100644 --- a/api/app/models/bookings/exam.py +++ b/api/app/models/bookings/exam.py @@ -13,6 +13,7 @@ limitations under the License.''' from app.models.bookings import Base +from app.utilities.sqlalchemy_compat import UtcDateTime from qsystem import db @@ -28,14 +29,14 @@ class Exam(Base): examinee_name = db.Column(db.String(50), nullable=True) examinee_email = db.Column(db.String(400), nullable=True) examinee_phone = db.Column(db.String(400), nullable=True) - expiry_date = db.Column(db.DateTime, nullable=True) + expiry_date = db.Column(UtcDateTime, nullable=True) notes = db.Column(db.String(400), nullable=True) - exam_received_date = db.Column(db.DateTime, nullable=True) + exam_received_date = db.Column(UtcDateTime, nullable=True) session_number = db.Column(db.Integer, nullable=True) number_of_students = db.Column(db.Integer, nullable=True) exam_method = db.Column(db.String(15), nullable=False) deleted_date = db.Column(db.String(50), nullable=True) - exam_returned_date = db.Column(db.DateTime, nullable=True) + exam_returned_date = db.Column(UtcDateTime, nullable=True) exam_returned_tracking_number = db.Column(db.String(255), nullable=True) exam_written_ind = db.Column(db.Integer, nullable=False, default=0) offsite_location = db.Column(db.String(50), nullable=True) diff --git a/api/app/models/theq/public_user.py b/api/app/models/theq/public_user.py index 7af252a4b..c172cc673 100644 --- a/api/app/models/theq/public_user.py +++ b/api/app/models/theq/public_user.py @@ -22,7 +22,7 @@ class PublicUser(Base): user_id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False) username = db.Column(db.String(100), unique=True, index=True) - last_name = db.Column(db.String(100)) + last_name = db.Column(db.String(200)) display_name = db.Column(db.String(200)) email = db.Column(db.String(200)) telephone = db.Column(db.String(20)) diff --git a/api/app/models/theq/service.py b/api/app/models/theq/service.py index c3664f0f0..e0d072c05 100644 --- a/api/app/models/theq/service.py +++ b/api/app/models/theq/service.py @@ -41,8 +41,8 @@ class Service(Base): display_dashboard_ind = db.Column(db.Integer, nullable=False) actual_service_ind = db.Column(db.Integer, nullable=False) - external_service_name = db.Column(db.String(100), nullable=True) - online_link = db.Column(db.String(200), nullable=True) + external_service_name = db.Column(db.String(500), nullable=True) + online_link = db.Column(db.String(500), nullable=True) online_availability = db.Column(Enum(Availability)) timeslot_duration = db.Column(db.Integer, nullable=True) is_dlkt = db.Column(Enum(YesNo)) diff --git a/api/migrations/versions/c388edd5dc5a_.py b/api/migrations/versions/c388edd5dc5a_.py deleted file mode 100644 index 36dcb9143..000000000 --- a/api/migrations/versions/c388edd5dc5a_.py +++ /dev/null @@ -1,207 +0,0 @@ -"""empty message - -Revision ID: c388edd5dc5a -Revises: 8b6c67545310 -Create Date: 2026-03-27 08:50:06.260961 - -""" - -import sqlalchemy as sa -import sqlalchemy_utc -from alembic import op -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision = "c388edd5dc5a" -down_revision = "8b6c67545310" -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table("exam", schema=None) as batch_op: - batch_op.alter_column( - "expiry_date", - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True, - ) - batch_op.alter_column( - "exam_received_date", - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True, - ) - batch_op.alter_column( - "exam_returned_date", - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True, - ) - - with op.batch_alter_table("publicuser", schema=None) as batch_op: - batch_op.alter_column( - "last_name", - existing_type=sa.VARCHAR(length=200), - type_=sa.String(length=100), - existing_nullable=True, - ) - - with op.batch_alter_table("service", schema=None) as batch_op: - batch_op.alter_column( - "external_service_name", - existing_type=sa.VARCHAR(length=500), - type_=sa.String(length=100), - existing_nullable=True, - ) - batch_op.alter_column( - "online_link", - existing_type=sa.VARCHAR(length=500), - type_=sa.String(length=200), - existing_nullable=True, - ) - - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table("service", schema=None) as batch_op: - batch_op.alter_column( - "online_link", - existing_type=sa.String(length=200), - type_=sa.VARCHAR(length=500), - existing_nullable=True, - ) - batch_op.alter_column( - "external_service_name", - existing_type=sa.String(length=100), - type_=sa.VARCHAR(length=500), - existing_nullable=True, - ) - - with op.batch_alter_table("publicuser", schema=None) as batch_op: - batch_op.alter_column( - "last_name", - existing_type=sa.String(length=100), - type_=sa.VARCHAR(length=200), - existing_nullable=True, - ) - - with op.batch_alter_table("exam", schema=None) as batch_op: - batch_op.alter_column( - "exam_returned_date", - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True, - ) - batch_op.alter_column( - "exam_received_date", - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True, - ) - batch_op.alter_column( - "expiry_date", - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True, - ) - - op.create_table( - "appointment_version", - sa.Column("appointment_id", sa.INTEGER(), autoincrement=False, nullable=False), - sa.Column("office_id", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column("service_id", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column("citizen_id", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column( - "start_time", - postgresql.TIMESTAMP(timezone=True), - autoincrement=False, - nullable=True, - ), - sa.Column( - "end_time", - postgresql.TIMESTAMP(timezone=True), - autoincrement=False, - nullable=True, - ), - sa.Column( - "checked_in_time", - postgresql.TIMESTAMP(timezone=True), - autoincrement=False, - nullable=True, - ), - sa.Column( - "comments", sa.VARCHAR(length=255), autoincrement=False, nullable=True - ), - sa.Column( - "citizen_name", sa.VARCHAR(length=255), autoincrement=False, nullable=True - ), - sa.Column( - "contact_information", - sa.VARCHAR(length=255), - autoincrement=False, - nullable=True, - ), - sa.Column( - "blackout_flag", sa.VARCHAR(length=1), autoincrement=False, nullable=True - ), - sa.Column( - "recurring_uuid", sa.VARCHAR(length=255), autoincrement=False, nullable=True - ), - sa.Column("online_flag", sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column("is_draft", sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column( - "created_at", - postgresql.TIMESTAMP(timezone=True), - autoincrement=False, - nullable=True, - ), - sa.Column("stat_flag", sa.BOOLEAN(), autoincrement=False, nullable=True), - sa.Column( - "updated_at", - postgresql.TIMESTAMP(timezone=True), - autoincrement=False, - nullable=True, - ), - sa.Column("updated_by", sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column("transaction_id", sa.BIGINT(), autoincrement=False, nullable=False), - sa.Column( - "end_transaction_id", sa.BIGINT(), autoincrement=False, nullable=True - ), - sa.Column("operation_type", sa.SMALLINT(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint( - "appointment_id", "transaction_id", name=op.f("appointment_version_pkey") - ), - ) - with op.batch_alter_table("appointment_version", schema=None) as batch_op: - batch_op.create_index( - batch_op.f("ix_appointment_version_transaction_id"), - ["transaction_id"], - unique=False, - ) - batch_op.create_index( - batch_op.f("ix_appointment_version_operation_type"), - ["operation_type"], - unique=False, - ) - batch_op.create_index( - batch_op.f("ix_appointment_version_end_transaction_id"), - ["end_transaction_id"], - unique=False, - ) - - op.create_table( - "transaction", - sa.Column( - "issued_at", postgresql.TIMESTAMP(), autoincrement=False, nullable=True - ), - sa.Column("id", sa.BIGINT(), autoincrement=True, nullable=False), - sa.Column( - "remote_addr", sa.VARCHAR(length=50), autoincrement=False, nullable=True - ), - sa.PrimaryKeyConstraint("id", name=op.f("transaction_pkey")), - ) - # ### end Alembic commands ### From 01b3f6a34ec9deb497564962bfe3c765db22778d Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Thu, 9 Apr 2026 12:03:15 -0700 Subject: [PATCH 36/81] Fix Snowplow context handling with raiseloaded citizen --- api/app/tests/flows/test_queue_flows.py | 44 +++++++++++++++++++++++++ api/app/utilities/snowplow.py | 10 ++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/api/app/tests/flows/test_queue_flows.py b/api/app/tests/flows/test_queue_flows.py index fd97618a5..ad338a54f 100644 --- a/api/app/tests/flows/test_queue_flows.py +++ b/api/app/tests/flows/test_queue_flows.py @@ -19,6 +19,7 @@ ) from app.tests.contracts.conftest import validate_schema from app.tests.contracts.schemas import CITIZEN_RESPONSE_SCHEMA +from sqlalchemy.orm import raiseload pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] @@ -495,6 +496,49 @@ def test_qt6_first_generic_invite_prefers_the_quick_transaction_counter( ) +def test_generic_invite_snowplow_context_handles_raiseloaded_citizen( + internal_ga_client, seeded_data, app +): + """Assert that SnowPlow can build citizen context from the generic invite query shape.""" + citizen, _service_request, _queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="SnowPlow Generic Invite Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="quick_trans", + qt_xn_citizen_ind=1, + ) + + with app.app_context(): + from app.models.theq import Citizen + from app.utilities.snowplow import SnowPlow + + citizen_model = ( + Citizen.query.options( + raiseload(Citizen.office), + raiseload(Citizen.counter), + raiseload(Citizen.user), + ) + .filter_by(citizen_id=citizen["citizen_id"]) + .first() + ) + + citizen_context = SnowPlow.get_citizen( + citizen_model, + "Counter", + svc_number=3, + ) + + assert citizen_context.data == { + "client_id": citizen["citizen_id"], + "service_count": 3, + "counter_type": "Quick Trans", + } + + def test_qt6_second_generic_invite_returns_the_remaining_standard_queue_citizen( internal_ga_client, seeded_data ): diff --git a/api/app/utilities/snowplow.py b/api/app/utilities/snowplow.py index 67e5656c7..dc756f8cd 100644 --- a/api/app/utilities/snowplow.py +++ b/api/app/utilities/snowplow.py @@ -14,6 +14,7 @@ from app.models.theq.channel import Channel from app.models.theq.citizen import Citizen +from app.models.theq.counter import Counter from app.models.theq.csr import CSR from app.models.theq.office import Office from app.models.theq.role import Role @@ -151,10 +152,13 @@ def failure(count, failed): def get_citizen(citizen_obj, counter_name, svc_number = 1): citizen_type = counter_name - if citizen_obj.office.sb.sb_type == "nocallonsmartboard": + office = db.session.get(Office, citizen_obj.office_id) + if office.sb.sb_type == "nocallonsmartboard": citizen_type = "Counter" - elif citizen_obj.counter is not None: - citizen_type = citizen_obj.counter.counter_name + elif citizen_obj.counter_id is not None: + counter = db.session.get(Counter, citizen_obj.counter_id) + if counter is not None: + citizen_type = counter.counter_name # Set up the citizen context. citizen = SelfDescribingJson('iglu:ca.bc.gov.cfmspoc/citizen/jsonschema/4-0-0', From b25c5f9a0d12bf8386ffa97937351ee33f338f85 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 17 Apr 2026 14:48:15 -0700 Subject: [PATCH 37/81] Fix exam schema validation --- .../bookings/exam/exam_event_id_detail.py | 21 +++-- api/app/schemas/bookings/exam_schema.py | 2 +- api/app/tests/contracts/schemas.py | 2 +- .../tests/flows/test_booking_exam_flows.py | 84 +++++++++++++++++++ api/app/tests/test_schema_compatibility.py | 79 ++++++++++++++++- api/qsystem.py | 10 +++ 6 files changed, 182 insertions(+), 16 deletions(-) diff --git a/api/app/resources/bookings/exam/exam_event_id_detail.py b/api/app/resources/bookings/exam/exam_event_id_detail.py index ecfc99bc5..8be42f32a 100644 --- a/api/app/resources/bookings/exam/exam_event_id_detail.py +++ b/api/app/resources/bookings/exam/exam_event_id_detail.py @@ -1,4 +1,4 @@ -'''Copyright 2018 Province of British Columbia +"""Copyright 2018 Province of British Columbia Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10,21 +10,21 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and -limitations under the License.''' +limitations under the License.""" import logging -from flask_restx import Resource -from sqlalchemy import exc + +from app.auth.auth import jwt from app.models.bookings import Exam from app.schemas.bookings import ExamSchema -from qsystem import api from app.utilities.auth_util import Role, has_any_role -from app.auth.auth import jwt +from flask_restx import Resource +from qsystem import api +from sqlalchemy import exc -@api.route("/exams/event_id//", methods=["GET"]) +@api.route("/exams/event_id//", methods=["GET"]) class ExamEventIDDetail(Resource): - exam_schema = ExamSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @@ -34,12 +34,11 @@ def get(self, id): exam = Exam.query.filter_by(event_id=str(id)).all() if not exam: - return {'message': False}, 200 + return {"message": False}, 200 else: - return {'message': True}, 200 + return {"message": True}, 200 except exc.SQLAlchemyError as error: - logging.error(error, exc_info=True) return {"message": "API is down"}, 500 diff --git a/api/app/schemas/bookings/exam_schema.py b/api/app/schemas/bookings/exam_schema.py index 7490b09ed..22884cff5 100644 --- a/api/app/schemas/bookings/exam_schema.py +++ b/api/app/schemas/bookings/exam_schema.py @@ -46,7 +46,7 @@ class Meta(BaseSchema.Meta): exam_method = fields.Str() exam_name = fields.Str() exam_received = fields.Int() - exam_received_date = fields.DateTime(allow_none=True, format='%Y-%m-%dT%H:%M:%SZ') + exam_received_date = fields.DateTime(allow_none=True) exam_type_id = fields.Int() examinee_name = fields.Str(allow_none=True) examinee_phone = fields.Str(allow_none=True) diff --git a/api/app/tests/contracts/schemas.py b/api/app/tests/contracts/schemas.py index 61403f3a5..f4f8d8f7c 100644 --- a/api/app/tests/contracts/schemas.py +++ b/api/app/tests/contracts/schemas.py @@ -501,7 +501,7 @@ def object_schema(*, required, properties, additional_properties=False): "exam_method": nullable({"type": "string"}), "exam_name": {"type": "string"}, "exam_received": nullable({"type": "integer"}), - "exam_received_date": nullable(UTC_DATETIME_SCHEMA), + "exam_received_date": nullable(ISO_DATETIME_SCHEMA), "exam_type_id": {"type": "integer"}, "examinee_name": nullable({"type": "string"}), "examinee_phone": nullable({"type": "string"}), diff --git a/api/app/tests/flows/test_booking_exam_flows.py b/api/app/tests/flows/test_booking_exam_flows.py index 700a21ea5..4215a59e6 100644 --- a/api/app/tests/flows/test_booking_exam_flows.py +++ b/api/app/tests/flows/test_booking_exam_flows.py @@ -146,6 +146,36 @@ def test_exam_can_be_listed_and_retrieved(internal_ga_client, seeded_data): ) +def test_exam_create_accepts_iso_offset_exam_received_date( + internal_ga_client, seeded_data +): + """Assert exam creation accepts frontend ISO offset timestamps.""" + response = internal_ga_client.post( + "/exams/", + json={ + "exam_method": "paper", + "exam_received_date": "2026-04-17T07:00:00+00:00", + "expiry_date": "2026-05-22T07:00:00+00:00", + "exam_type_id": seeded_data["exam_type_id"], + "event_id": "test1234887", + "exam_name": "test exam", + "examinee_name": "test candidate", + "notes": "test notes", + "office_id": seeded_data["office_ids"]["test_office"], + "payee_ind": 0, + "receipt_sent_ind": 0, + "sbc_managed_ind": 0, + "exam_returned_ind": 0, + "exam_written_ind": 0, + "number_of_students": 1, + }, + ) + body = json_of(response) + + assert_json_response(response, 201) + assert body["exam"]["exam_received_date"] == "2026-04-17T07:00:00+00:00" + + def test_exam_can_be_updated(internal_ga_client, seeded_data): """Assert that exam updates preserve editable exam fields.""" booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) @@ -162,6 +192,60 @@ def test_exam_can_be_updated(internal_ga_client, seeded_data): assert json_of(response)["exam"]["exam_name"] == "Updated exam name" +def test_exam_update_accepts_iso_offset_exam_received_date( + internal_ga_client, seeded_data +): + """Assert exam updates accept frontend ISO offset timestamps.""" + booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) + exam = _create_exam( + internal_ga_client, + seeded_data, + booking["booking_id"], + event_id="event-upd-recv-date", + ) + + response = internal_ga_client.put( + f"/exams/{exam['exam_id']}/", + json={"exam_received_date": "2026-04-17T07:00:00+00:00"}, + ) + + assert_json_response(response, 201) + assert json_of(response)["exam"]["exam_received_date"] == ( + "2026-04-17T07:00:00+00:00" + ) + + +def test_exam_create_invalid_datetime_returns_validation_response( + internal_ga_client, seeded_data +): + """Assert schema load errors return a controlled JSON 422 response.""" + response = internal_ga_client.post( + "/exams/", + json={ + "exam_method": "paper", + "exam_received_date": "not-a-date", + "expiry_date": "2026-05-22T07:00:00+00:00", + "exam_type_id": seeded_data["exam_type_id"], + "event_id": "event-invalid-date", + "exam_name": "Invalid date exam", + "examinee_name": "test candidate", + "notes": "test notes", + "office_id": seeded_data["office_ids"]["test_office"], + "payee_ind": 0, + "receipt_sent_ind": 0, + "sbc_managed_ind": 0, + "exam_returned_ind": 0, + "exam_written_ind": 0, + "number_of_students": 1, + }, + ) + + assert_json_response(response, 422) + assert json_of(response) == { + "message": {"exam_received_date": ["Not a valid datetime."]} + } + + def test_exam_event_lookup_returns_the_matching_exam(internal_ga_client, seeded_data): """Assert that exam event lookups continue to find the matching exam.""" booking = _create_booking(internal_ga_client, seeded_data, days_from_now=5) diff --git a/api/app/tests/test_schema_compatibility.py b/api/app/tests/test_schema_compatibility.py index 15e27f175..071962516 100644 --- a/api/app/tests/test_schema_compatibility.py +++ b/api/app/tests/test_schema_compatibility.py @@ -1,6 +1,8 @@ +import ast import importlib import inspect import pkgutil +import sys from datetime import datetime, timezone import pytest @@ -99,8 +101,8 @@ def test_service_schema_serializes_parent_name(app, seeded_data): assert dumped["is_dlkt"] is True -def test_exam_schema_preserves_exam_received_date_format(app, seeded_data): - """Assert that exam received dates keep their UTC contract format during serialization.""" +def test_exam_schema_uses_iso_exam_received_date_format(app, seeded_data): + """Assert that exam received dates use Marshmallow's normal ISO contract.""" with app.app_context(): from app.models.bookings import Exam, ExamType, Invigilator from app.models.theq import Office @@ -131,7 +133,78 @@ def test_exam_schema_preserves_exam_received_date_format(app, seeded_data): dumped = ExamSchema().dump(exam) - assert dumped["exam_received_date"] == "2026-03-25T12:30:00Z" + assert dumped["exam_received_date"] == "2026-03-25T12:30:00+00:00" + + +def test_exam_schema_accepts_iso_exam_received_date_inputs(app, seeded_data): + """Assert that frontend ISO offset dates load without strict literal-Z parsing.""" + with app.app_context(): + from app.schemas.bookings import ExamSchema + + base_payload = { + "exam_method": "paper", + "expiry_date": "2026-05-22T07:00:00+00:00", + "exam_type_id": seeded_data["exam_type_id"], + "event_id": "test1234887", + "exam_name": "test exam", + "examinee_name": "test candidate", + "notes": "test notes", + "office_id": seeded_data["office_ids"]["test_office"], + "payee_ind": 0, + "receipt_sent_ind": 0, + "sbc_managed_ind": 0, + "exam_returned_ind": 0, + "exam_written_ind": 0, + "number_of_students": 1, + } + + offset_exam = ExamSchema().load( + { + **base_payload, + "exam_received_date": "2026-04-17T07:00:00+00:00", + } + ) + z_exam = ExamSchema().load( + { + **base_payload, + "exam_received_date": "2026-04-17T07:00:00Z", + } + ) + + assert offset_exam.exam_received_date.isoformat() == ( + "2026-04-17T07:00:00+00:00" + ) + assert z_exam.exam_received_date.isoformat() == "2026-04-17T07:00:00+00:00" + + +def test_no_schema_explicitly_uses_strict_literal_z_datetime_format(app, seeded_database): + """Assert DateTime fields do not opt into literal-Z-only parsing.""" + del seeded_database + + with app.app_context(): + strict_fields = [] + for schema_cls in _schema_classes(): + source = inspect.getsource(sys.modules[schema_cls.__module__]) + tree = ast.parse(source) + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + if not ( + isinstance(node.func, ast.Attribute) + and node.func.attr == "DateTime" + and isinstance(node.func.value, ast.Name) + and node.func.value.id == "fields" + ): + continue + for keyword in node.keywords: + if ( + keyword.arg == "format" + and isinstance(keyword.value, ast.Constant) + and keyword.value.value == "%Y-%m-%dT%H:%M:%SZ" + ): + strict_fields.append(f"{schema_cls.__module__}:{node.lineno}") + + assert strict_fields == [] def test_booking_schema_post_dump_supports_single_and_many(app, seeded_data): diff --git a/api/qsystem.py b/api/qsystem.py index d44b697c5..d7f7c3af6 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -19,6 +19,7 @@ from flask_sqlalchemy import SQLAlchemy from flask_socketio import SocketIO from functools import wraps +from marshmallow import ValidationError from sqlalchemy.exc import SQLAlchemyError from app.exceptions import AuthError from app.utilities.flask_admin_compat import apply_wtforms_compat @@ -365,6 +366,15 @@ def error_handler(e): return "error" +@application.errorhandler(ValidationError) +@api.errorhandler(ValidationError) +def handle_validation_error(error): + logging.warning("Validation error: %s", error.messages) + response = {"message": error.messages} + error.data = response + return response, 422 + + @application.errorhandler(AuthError) @api.errorhandler(AuthError) def handle_auth_error(ex): From 97aadf3c0731988978f62ad0b33f3932bad0116a Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Thu, 23 Apr 2026 14:14:30 -0700 Subject: [PATCH 38/81] Add second local appointment user Co-authored-by: Copilot --- README.md | 2 +- keycloak-local/servicebc-local-realm.json | 35 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e2c98f296..5644ff714 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ The devcontainer installs dependencies automatically, applies database migration - Base URL: `http://localhost:8085/auth` - Admin console: `http://localhost:8085/auth/admin/` - Admin credentials: `admin` / `password` - - Demo users: `democsr@idir`, `demoga@idir`, `admin@idir`, `citizen@bceidboth` + - Demo users: `democsr@idir`, `demoga@idir`, `admin@idir`, `citizen@bceidboth`, `citizen2@bceidboth` - Demo user password: `password` - Confidential client id: `theq-queue-management-api` - Confidential client secret: `theq-local-dev-secret` diff --git a/keycloak-local/servicebc-local-realm.json b/keycloak-local/servicebc-local-realm.json index c0d2eac7a..d7a972105 100644 --- a/keycloak-local/servicebc-local-realm.json +++ b/keycloak-local/servicebc-local-realm.json @@ -580,6 +580,41 @@ "realmRoles": [ "online_appointment_user" ] + }, + { + "username": "citizen2@bceidboth", + "enabled": true, + "emailVerified": true, + "firstName": "Test2", + "lastName": "Citizen2", + "email": "citizen2@example.com", + "attributes": { + "theq_username": [ + "citizen2" + ], + "identity_provider": [ + "bceid" + ], + "display_name": [ + "Test2 Citizen2" + ], + "userid": [ + "CITIZEN2" + ], + "name": [ + "Test2 Citizen2" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "online_appointment_user" + ] } ] } From 65e0b35037d8f0c8c251623a048093f7b68c66fb Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Thu, 23 Apr 2026 14:59:07 -0700 Subject: [PATCH 39/81] Fix load testing script --- tests/loadtesting/README.md | 61 +- tests/loadtesting/csr-http.yaml | 20 +- tests/loadtesting/csr-socket.yaml | 4 +- tests/loadtesting/csr-test-all.yaml | 29 +- tests/loadtesting/envs.example.sh | 33 +- tests/loadtesting/functions.js | 316 +- tests/loadtesting/package-lock.json | 14245 ++++++++++++++++++++++++++ tests/loadtesting/package.json | 8 +- tests/loadtesting/run-artillery.js | 56 + 9 files changed, 14682 insertions(+), 90 deletions(-) create mode 100644 tests/loadtesting/package-lock.json create mode 100644 tests/loadtesting/run-artillery.js diff --git a/tests/loadtesting/README.md b/tests/loadtesting/README.md index c884b2a56..3386dbe9f 100644 --- a/tests/loadtesting/README.md +++ b/tests/loadtesting/README.md @@ -34,6 +34,34 @@ chmod +x envs.sh pip install py-spy ``` +### Local prerequisites + +This load test harness runs against the current local stack in this repository. + +Before running it, make sure you have: + +```bash +# Start the local Keycloak realm +docker compose up -d keycloak + +# In the API workspace, point at your local Postgres, migrate, then seed demo data +cd ../../api +uv run python manage.py db upgrade +uv run python manage.py bootstrap +``` + +Important local defaults: + +* API target: `http://localhost:5000` +* Keycloak base URL: `http://localhost:8085/auth` +* Keycloak realm: `servicebc-local` +* Keycloak client id: `theq-queue-management-api` +* Keycloak client secret: `theq-local-dev-secret` +* Demo load-test user: `admin@idir` +* Demo load-test password: `password` + +`bootstrap` wipes and recreates development data. The default load test IDs in `envs.sh` assume that freshly bootstrapped dataset. + ## Usage We have created a number of npm scripts in `package.json` that expose the functionality we worked on. For example, `npm run tests:all` runs all loadtesting - websocket and HTTP. There is also `tests:http` and `tests:socket`. @@ -43,7 +71,7 @@ Quick reference: ```bash # just runs load testing - no python profiling -npm run tests:all +npm run tests:all # run both load testing and python profiling: npm run python:profile # in one terminal, start profiling @@ -59,7 +87,14 @@ More details can be found [in python profiling](#python-profiling) Configuration of varables is done in `envs.sh`. The main variables that will be used are * `MAX_VIRTUAL_USERS` - determines the maximum amount of concurrent virtual users that are accessing the system at once -* `TARGET` - the endpoint being load tested. This is configured to use the dev OIDC keycloak, so it can freely be changed between localhost and OpenShift dev. +* `TARGET` - the endpoint being load tested. The default is the local API at `http://localhost:5000` +* `KEYCLOAK_BASE_URL`, `KEYCLOAK_REALM`, `KEYCLOAK_CLIENT_ID`, `KEYCLOAK_CLIENT_SECRET` - local Keycloak token settings +* `KEYCLOAK_USERNAME`, `KEYCLOAK_PASSWORD` - the demo user used to mint the token for the load test +* `LOADTEST_OFFICE_ID`, `LOADTEST_CREATE_SERVICE_ID`, `LOADTEST_UPDATE_SERVICE_ID` - seeded local data IDs used by the appointment scenarios +* `LOADTEST_DRAFT_OFFICE_ID`, `LOADTEST_DRAFT_SERVICE_ID` - seeded local data IDs used for draft creation. The local bootstrap default uses office `2` because office `1` has no appointment timeslots. +* `LOADTEST_OFFICE_TIMEZONE` - IANA timezone used to turn slot times into draft appointment timestamps. The local bootstrap default is `America/Vancouver`. +* `LOADTEST_DRAFT_OFFICE_TIMEZONE` - IANA timezone for the draft office. The local bootstrap default is `America/Creston` for office `2`. +* `LOADTEST_DRAFT_SLOT_WEEK_RANGE` - size of the random future-week range used for generated draft windows. The default preserves the sampled slot weekday/time while avoiding concurrent VUs reserving the same appointment window. ### Running Load Tests @@ -85,7 +120,7 @@ Python profiling allows us to get an in-depth look at where the Python API proce All Python commands must be run on the same machine that already has the API running. Additionally, they cannot be run in OpenShift itself due to security constraints [(see more)](#python-profiling-on-openshift) -These commands must be run on the same machine that already has the API running. It will ask for `sudo` password. It will scan for the parent gunicorn process by name and profile it. Let this command run for the durattion of the profiling. Typically, you start profiling, then run load testing, then exit profiling. +These commands must be run on the same machine that already has the API running. It will ask for `sudo` password. It will scan for the parent gunicorn process by name and profile it. Let this command run for the durattion of the profiling. Typically, you start profiling, then run load testing, then exit profiling. **Why sudo?** *The profiler we use, `pyspy` can profile already running Python processes. This is great for profiling real world performance, but sudo is required in order to spy on another process. This is Linux security to stop a non-sudo process from inspecting/modifying other processes which should not be allowed.* @@ -106,7 +141,7 @@ We have included an example report, called `profile-demo-withloadtesting.svg` Summary - we cannot run this profiling on OpenShift due to BCGov specific security configurations. -In order to profile another process on OpenShift, we need to make the below change and add `SYS_PTRACE`. +In order to profile another process on OpenShift, we need to make the below change and add `SYS_PTRACE`. ```yaml securityContext: @@ -250,11 +285,11 @@ Running with 400 conurrent users caused many more errors: * Auth taking up 42.25% of all requests. Only ~12% of CPU spent on business logic. * Send emails on separate pod -**Load Balancing not working** - it appears that load balancing between API pods is not working. Generally speaking (depending on load balancing config), when a new request comes in it should be distributed equally between all available pods. This is not happening. Instead, even though we have 2 API pods, all of the traffic is going to 1 pod. This is a huge problem, as it means none of the horizontal scaling on OpenShift is working. There may be some technical hurdles, for example, there have been discussions before that perhaps load balancing would split the websocket sessions. +**Load Balancing not working** - it appears that load balancing between API pods is not working. Generally speaking (depending on load balancing config), when a new request comes in it should be distributed equally between all available pods. This is not happening. Instead, even though we have 2 API pods, all of the traffic is going to 1 pod. This is a huge problem, as it means none of the horizontal scaling on OpenShift is working. There may be some technical hurdles, for example, there have been discussions before that perhaps load balancing would split the websocket sessions. -**Recommendation Enable load balancing so that load is distributed equally among pods:** i.e. `roundrobin` [load balancing strategy - OpenShift docs link](https://docs.openshift.com/container-platform/3.5/architecture/core_concepts/routes.html#load-balancing). +**Recommendation Enable load balancing so that load is distributed equally among pods:** i.e. `roundrobin` [load balancing strategy - OpenShift docs link](https://docs.openshift.com/container-platform/3.5/architecture/core_concepts/routes.html#load-balancing). -**Authentication taking up 42.25% of CPU on every request** - If we look at the flamegraph, for example, `profile-demo-withloadtesting.svg`, we can see that 36.75% of the CPU was spent on just parsing the OIDC token from Keycloak, and another 5.5% was spent in our `auth_util`. Only 12% was for actual busienss logic. Another ~10% for JSON encoding, and the remaining ~35% for threading, eventlet, and gunicorn. +**Authentication taking up 42.25% of CPU on every request** - If we look at the flamegraph, for example, `profile-demo-withloadtesting.svg`, we can see that 36.75% of the CPU was spent on just parsing the OIDC token from Keycloak, and another 5.5% was spent in our `auth_util`. Only 12% was for actual busienss logic. Another ~10% for JSON encoding, and the remaining ~35% for threading, eventlet, and gunicorn. **Recommendation: Refactor the `has_any_role`** function in `auth_util.py`, as it is in the hot path for basically every single request. Profile changes before and after to measure impact. While it's only 5% of CPU, it's 5% of CPU for _every request_, so any improvements here will benefit across the board. @@ -273,17 +308,23 @@ Running with 400 conurrent users caused many more errors: ### Verify IDs are correct -The first thing to check when trouble shooting is that the IDs in `csr-test-all.yaml` are correct. For example, `service_id` and `office_id` we have hardcoded to 7 and 1 respectively. +The first thing to check when troubleshooting is that the IDs in `envs.sh` match your local seeded data. + +The default values assume `uv run python manage.py bootstrap` has been run locally: + +* `LOADTEST_OFFICE_ID=1` +* `LOADTEST_CREATE_SERVICE_ID=11` +* `LOADTEST_UPDATE_SERVICE_ID=7` ### Verify the "admin" user is assigned to correct office The admin user must be assigned to the same office that the tests try to use. -For example, if you set `office_id` to 1, then admin must be assigned to Test Office (assuming 1 = Test Office). +For example, if `LOADTEST_OFFICE_ID=1`, then the app-level `admin` CSR record must still be assigned to Test Office. With a fresh local bootstrap, that is the default arrangement. ### I get errors when testing locally, but not when testing OpenShift dev This is normal. You run into performance limits quicker on a weaker local machine. -OpenShift dev can support 200 concurrent users, but my laptop can support far less than that. \ No newline at end of file +OpenShift dev can support 200 concurrent users, but my laptop can support far less than that. diff --git a/tests/loadtesting/csr-http.yaml b/tests/loadtesting/csr-http.yaml index ff2560b95..0c9a58d94 100644 --- a/tests/loadtesting/csr-http.yaml +++ b/tests/loadtesting/csr-http.yaml @@ -32,17 +32,15 @@ scenarios: - get: url: "/api/v1/channels/" - get: - url: "/api/v1/services/?office_id=1" + url: "/api/v1/services/?office_id={{ $processEnvironment.LOADTEST_OFFICE_ID }}" - log: "exam_types running" - get: url: "/api/v1/exam_types/" - log: "creating draft appointmen running" - post: + beforeRequest: "setDraftPayload" url: "/api/v1/appointments/draft" - json: - start_time: "2020-11-05T18:00:00Z" - end_time: "2020-11-05T18:15:00Z" - office_id: 7 + json: {} capture: json: "$.appointment.appointment_id" as: "draftappointment_Id" @@ -52,31 +50,31 @@ scenarios: url: "/api/v1/appointments/draft/{{draftappointment_Id}}/" - log: "post appointments appointment running" - post: + beforeRequest: "setCreateAppointmentPayload" # url: "/api/v1/appointments/draft" url: "/api/v1/appointments/" json: start_time: "2020-11-05T18:00:00Z" end_time: "2020-11-05T18:15:00Z" - service_id: 21 + service_id: 11 citizen_name: "name from test" contact_information: "" - office_id: 7 - appointment_draft_id: 1 + office_id: 1 capture: json: "$.appointment.appointment_id" as: "appointment_id" strict: false # We don't mind if id can't be captured and the next requests 404s - log: "updating appointment {{appointment_id}} running" - put: + beforeRequest: "setUpdateAppointmentPayload" url: "/api/v1/appointments/{{appointment_id}}/" json: start_time: "2020-11-05T18:00:00Z" end_time: "2020-11-05T18:15:00Z" - service_id: 21 + service_id: 7 citizen_name: "updated name from test" contact_information: "" - office_id: 7 - appointment_draft_id: 1 + office_id: 1 capture: json: "$.appointment.appointment_id" as: "updateappointment_id" diff --git a/tests/loadtesting/csr-socket.yaml b/tests/loadtesting/csr-socket.yaml index 0359c9dfa..71cb32634 100644 --- a/tests/loadtesting/csr-socket.yaml +++ b/tests/loadtesting/csr-socket.yaml @@ -33,11 +33,11 @@ scenarios: flow: - log: " socket connectiong starting" - emit: - channel: "connect" + channel: "connecting" data: "ping" - think: 1 - emit: channel: "joinRoom" data: "joinRoom" - log: "joinRoom socket" - - think: 1 \ No newline at end of file + - think: 1 diff --git a/tests/loadtesting/csr-test-all.yaml b/tests/loadtesting/csr-test-all.yaml index 4b001896a..67204a40a 100644 --- a/tests/loadtesting/csr-test-all.yaml +++ b/tests/loadtesting/csr-test-all.yaml @@ -45,7 +45,7 @@ scenarios: - name: "CSR – Login, load data, idle" beforeRequest: 'setAuthHeader' weight: 75 - flow: &csrLoadData + flow: - get: url: "/api/v1/citizens/" - get: @@ -55,7 +55,7 @@ scenarios: - get: url: "/api/v1/channels/" - get: - url: "/api/v1/services/?office_id=1" + url: "/api/v1/services/?office_id={{ $processEnvironment.LOADTEST_OFFICE_ID }}" - get: url: "/api/v1/exam_types/" - think: 5 @@ -66,14 +66,23 @@ scenarios: beforeRequest: 'setAuthHeader' weight: 75 flow: - # Copy the same flow for idle CSRs, and just add more steps - - *csrLoadData + - get: + url: "/api/v1/citizens/" + - get: + url: "/api/v1/appointments/" + - get: + url: "/api/v1/categories/" + - get: + url: "/api/v1/channels/" + - get: + url: "/api/v1/services/?office_id={{ $processEnvironment.LOADTEST_OFFICE_ID }}" + - get: + url: "/api/v1/exam_types/" + - think: 5 - post: + beforeRequest: "setDraftPayload" url: "/api/v1/appointments/draft" - json: - start_time: "2020-11-05T18:00:00Z" - end_time: "2020-11-05T18:15:00Z" - office_id: 1 + json: {} capture: json: "$.appointment.appointment_id" as: "draftappointment_Id" @@ -83,6 +92,7 @@ scenarios: url: "/api/v1/appointments/draft/{{draftappointment_Id}}/" - think: 5 - post: + beforeRequest: "setCreateAppointmentPayload" # url: "/api/v1/appointments/draft" url: "/api/v1/appointments/" json: @@ -92,13 +102,13 @@ scenarios: citizen_name: "name from test" contact_information: "" office_id: 1 - appointment_draft_id: 1 capture: json: "$.appointment.appointment_id" as: "appointment_id" strict: false # We don't mind if id can't be captured and the next requests 404s - think: 5 - put: + beforeRequest: "setUpdateAppointmentPayload" url: "/api/v1/appointments/{{appointment_id}}/" json: start_time: "2020-11-05T18:00:00Z" @@ -107,7 +117,6 @@ scenarios: citizen_name: "updated name from test" contact_information: "" office_id: 1 - appointment_draft_id: 1 capture: json: "$.appointment.appointment_id" as: "updateappointment_id" diff --git a/tests/loadtesting/envs.example.sh b/tests/loadtesting/envs.example.sh index 843be05e0..fa6d2b95a 100644 --- a/tests/loadtesting/envs.example.sh +++ b/tests/loadtesting/envs.example.sh @@ -2,21 +2,28 @@ set -e set -u -# Set environment variables below. echo "Loading environment variables" -# We update the API key from keycloak -export SERVICE_API_KEY=$(npm run get-keycloak-token --silent) export MAX_VIRTUAL_USERS=200 - -# Note - Changing target may require additional keycloak changes, depending on setup. -# Currently this is configured to work with Keycloak/OIDC dev in OpenShifit DEV -# and even localhost if your loaclhost uses OIDC dev. -# Load-testing logs into keycloak via `admin:admin`. -# Implementation details, including Keycloak URI, are in `function.js` export TARGET="http://localhost:5000" -# export TARGET="https://dev-theq.pathfinder.gov.bc.ca" -export KEYCLOAK_USERNAME='admin' -export KEYCLOAK_PASSWORD='admin' +export KEYCLOAK_BASE_URL="http://localhost:8085/auth" +export KEYCLOAK_REALM="servicebc-local" +export KEYCLOAK_CLIENT_ID="theq-queue-management-api" +export KEYCLOAK_CLIENT_SECRET="theq-local-dev-secret" +export KEYCLOAK_USERNAME="admin@idir" +export KEYCLOAK_PASSWORD="password" + +# These IDs assume a local database seeded with `uv run python manage.py bootstrap`. +export LOADTEST_OFFICE_ID=1 +export LOADTEST_CREATE_SERVICE_ID=11 +export LOADTEST_UPDATE_SERVICE_ID=7 +export LOADTEST_DRAFT_OFFICE_ID=2 +export LOADTEST_DRAFT_SERVICE_ID=11 +export LOADTEST_OFFICE_TIMEZONE="America/Vancouver" +export LOADTEST_DRAFT_OFFICE_TIMEZONE="America/Creston" +export LOADTEST_DRAFT_SLOT_WEEK_RANGE=300000 + +# Resolve a fresh access token after the Keycloak variables are exported. +export SERVICE_API_KEY="$(npm run get-keycloak-token --silent)" -echo "Target: $TARGET\n" \ No newline at end of file +echo "Target: $TARGET" diff --git a/tests/loadtesting/functions.js b/tests/loadtesting/functions.js index 5bf92ffa7..863621280 100644 --- a/tests/loadtesting/functions.js +++ b/tests/loadtesting/functions.js @@ -1,68 +1,304 @@ - const fetch = require('node-fetch'); +const { randomInt } = require('crypto'); -//KEYCLOAK_URI = 'https://dev.oidc.gov.bc.ca/auth' -KEYCLOAK_URI = 'https://dev.loginproxy.gov.bc.ca/auth' -KEYCLOAK_CLIENT = 'cfms-dev-staff' +const DEFAULT_KEYCLOAK_BASE_URL = 'http://localhost:8085/auth'; +const DEFAULT_KEYCLOAK_REALM = 'servicebc-local'; +const DEFAULT_KEYCLOAK_CLIENT_ID = 'theq-queue-management-api'; +const DEFAULT_KEYCLOAK_CLIENT_SECRET = 'theq-local-dev-secret'; +const DEFAULT_KEYCLOAK_USERNAME = 'admin@idir'; +const DEFAULT_KEYCLOAK_PASSWORD = 'password'; +const DEFAULT_TARGET = 'http://localhost:5000'; +const DEFAULT_OFFICE_TIMEZONE = 'America/Vancouver'; +const DEFAULT_DRAFT_OFFICE_ID = 2; +const DEFAULT_DRAFT_SERVICE_ID = 11; +const DEFAULT_DRAFT_OFFICE_TIMEZONE = 'America/Creston'; +const DEFAULT_DRAFT_SLOT_WEEK_RANGE = 300000; // This implementation assumes it never has to refresh a token and they never expire // As most load testing is short lived (minutes, not hours) this works fine. // Cache auth tokens to a plain old JavaScript object -const authTokenList = {} -async function getAuthToken(username, password){ - if (authTokenList[username]) return authTokenList[username]; +const authTokenList = {}; + +function getKeycloakConfig() { + return { + baseUrl: process.env.KEYCLOAK_BASE_URL || DEFAULT_KEYCLOAK_BASE_URL, + realm: process.env.KEYCLOAK_REALM || DEFAULT_KEYCLOAK_REALM, + clientId: process.env.KEYCLOAK_CLIENT_ID || DEFAULT_KEYCLOAK_CLIENT_ID, + clientSecret: process.env.KEYCLOAK_CLIENT_SECRET || DEFAULT_KEYCLOAK_CLIENT_SECRET, + }; +} + +function getKeycloakCredentials() { + return { + username: process.env.KEYCLOAK_USERNAME || DEFAULT_KEYCLOAK_USERNAME, + password: process.env.KEYCLOAK_PASSWORD || DEFAULT_KEYCLOAK_PASSWORD, + }; +} + +function getLoadTestConfig() { + const officeId = Number(process.env.LOADTEST_OFFICE_ID || 1); + const createServiceId = Number(process.env.LOADTEST_CREATE_SERVICE_ID || 11); + + return { + target: process.env.TARGET || DEFAULT_TARGET, + officeId, + createServiceId, + updateServiceId: Number(process.env.LOADTEST_UPDATE_SERVICE_ID || 7), + draftOfficeId: Number(process.env.LOADTEST_DRAFT_OFFICE_ID || DEFAULT_DRAFT_OFFICE_ID), + draftServiceId: Number(process.env.LOADTEST_DRAFT_SERVICE_ID || DEFAULT_DRAFT_SERVICE_ID), + officeTimezone: process.env.LOADTEST_OFFICE_TIMEZONE || DEFAULT_OFFICE_TIMEZONE, + draftOfficeTimezone: process.env.LOADTEST_DRAFT_OFFICE_TIMEZONE || DEFAULT_DRAFT_OFFICE_TIMEZONE, + draftSlotWeekRange: Number( + process.env.LOADTEST_DRAFT_SLOT_WEEK_RANGE || DEFAULT_DRAFT_SLOT_WEEK_RANGE + ), + }; +} + +function buildApiUrl(target, path) { + return new URL(path, target.replace(/\/$/, '')).toString(); +} + +function parseSlotDate(day, time, timezone) { + const [month, date, year] = day.split('/').map(Number); + const [hour, minute] = time.split(':').map(Number); + const utcGuess = Date.UTC(year, month - 1, date, hour, minute); + const localParts = new Intl.DateTimeFormat('en-CA', { + timeZone: timezone, + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hourCycle: 'h23', + }).formatToParts(new Date(utcGuess)).reduce((parts, part) => { + parts[part.type] = part.value; + return parts; + }, {}); + const zonedAsUtc = Date.UTC( + Number(localParts.year), + Number(localParts.month) - 1, + Number(localParts.day), + Number(localParts.hour), + Number(localParts.minute), + Number(localParts.second) + ); + const timezoneOffset = zonedAsUtc - utcGuess; + + return new Date(utcGuess - timezoneOffset).toISOString(); +} + +function findFirstAvailableSlot(slotsByDay) { + for (const [day, slots] of Object.entries(slotsByDay)) { + if (!Array.isArray(slots)) { + continue; + } + const slot = slots.find(({ no_of_slots }) => no_of_slots > 0); + if (slot) { + return { day, slot }; + } + } + + return null; +} + +function addDaysToSlotDay(day, daysToAdd) { + const [month, date, year] = day.split('/').map(Number); + const slotDate = new Date(Date.UTC(year, month - 1, date)); + slotDate.setUTCDate(slotDate.getUTCDate() + daysToAdd); + + return `${String(slotDate.getUTCMonth() + 1).padStart(2, '0')}/${String(slotDate.getUTCDate()).padStart(2, '0')}/${slotDate.getUTCFullYear()}`; +} + +function buildDraftPayloadFromSlot(day, slot) { + const { draftOfficeId, draftServiceId, draftOfficeTimezone, draftSlotWeekRange } = getLoadTestConfig(); + const sequencedDay = addDaysToSlotDay(day, randomInt(1, draftSlotWeekRange + 1) * 7); + return { + office_id: draftOfficeId, + service_id: draftServiceId, + start_time: parseSlotDate(sequencedDay, slot.start_time, draftOfficeTimezone), + end_time: parseSlotDate(sequencedDay, slot.end_time, draftOfficeTimezone), + }; +} + +async function getDraftAppointmentPayload() { + const { target, draftOfficeId, draftServiceId } = getLoadTestConfig(); + const slotsUrl = buildApiUrl( + target, + `/api/v1/offices/${draftOfficeId}/slots/?service_id=${draftServiceId}` + ); + const res = await fetch(slotsUrl); + const responseBody = await res.text(); + let slotsByDay; + + try { + slotsByDay = JSON.parse(responseBody); + } catch (error) { + throw new Error(`Unable to parse load-test appointment slots: ${responseBody}`); + } + + if (!res.ok) { + throw new Error(`Unable to fetch load-test appointment slots: ${res.status} ${JSON.stringify(slotsByDay)}`); + } + + const available = findFirstAvailableSlot(slotsByDay); + if (!available) { + throw new Error( + `No load-test appointment slots available for office ${draftOfficeId} and service ${draftServiceId}` + ); + } + + return buildDraftPayloadFromSlot(available.day, available.slot); +} + +function getAuthCacheKey(username) { + const { baseUrl, realm, clientId } = getKeycloakConfig(); + return `${baseUrl}|${realm}|${clientId}|${username}`; +} + +async function getAuthToken(username, password) { + const cacheKey = getAuthCacheKey(username); + if (authTokenList[cacheKey]) { + return authTokenList[cacheKey]; + } const newToken = await loginToKeycloak(username, password); - authTokenList[username] = newToken; + authTokenList[cacheKey] = newToken; return newToken; } -// Working async function loginToKeycloak(username, password) { - const res = await fetch(`${KEYCLOAK_URI}/realms/vtkayq4c/protocol/openid-connect/token`, { - "headers": { - "accept": "*/*", - "accept-language": "en-US,en;q=0.9", - "content-type": "application/x-www-form-urlencoded", - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "sec-fetch-site": "same-site" + const { baseUrl, realm, clientId, clientSecret } = getKeycloakConfig(); + const formData = new URLSearchParams({ + grant_type: 'password', + username, + password, + client_id: clientId, + }); + + if (clientSecret) { + formData.set('client_secret', clientSecret); + } + + const res = await fetch(`${baseUrl}/realms/${realm}/protocol/openid-connect/token`, { + headers: { + accept: '*/*', + 'content-type': 'application/x-www-form-urlencoded', }, - "body": `grant_type=password&username=${username}&password=${password}&client_id=${KEYCLOAK_CLIENT}`, - "method": "POST", - "mode": "cors", - "credentials": "include" - }) - const body = await res.json(); - return body; + body: formData.toString(), + method: 'POST', + }); + + const tokenResponse = await res.json(); + + if (!res.ok || !tokenResponse.access_token) { + throw new Error(`Unable to fetch Keycloak token: ${JSON.stringify(tokenResponse)}`); + } + + return tokenResponse; +} + +async function applyAuthHeader(requestParams) { + const { username, password } = getKeycloakCredentials(); + const { access_token } = await getAuthToken(username, password); + + requestParams.headers = requestParams.headers || {}; + requestParams.headers.Authorization = `Bearer ${access_token}`; + requestParams.headers.cookie = `oidc-jwt=${access_token}`; } async function setAuthHeader(requestParams, context, ee, next) { - const {access_token} = await getAuthToken(process.env.KEYCLOAK_USERNAME, process.env.KEYCLOAK_PASSWORD) - requestParams = {}; - requestParams.headers = {}; - requestParams.Authorization = {}; - requestParams.cookie = {}; - // HTTP requests use Authorization - requestParams.headers.Authorization = `Bearer ${access_token}` - // Websocket requests use oidc-jwt cookie - requestParams.headers.cookie = `oidc-jwt=${access_token}` - next(); + try { + await applyAuthHeader(requestParams); + if (typeof next === 'function') { + next(); + } + } catch (error) { + if (typeof next === 'function') { + next(error); + return; + } + throw error; + } +} + +async function setDraftPayload(requestParams, context, ee, next) { + try { + await applyAuthHeader(requestParams); + const draftPayload = await getDraftAppointmentPayload(); + requestParams.json = requestParams.json || {}; + Object.assign(requestParams.json, draftPayload); + if (typeof next === 'function') { + next(); + } + } catch (error) { + if (typeof next === 'function') { + next(error); + return; + } + throw error; + } +} + +async function setCreateAppointmentPayload(requestParams, context, ee, next) { + try { + await applyAuthHeader(requestParams); + const { officeId, createServiceId } = getLoadTestConfig(); + requestParams.json = requestParams.json || {}; + requestParams.json.office_id = officeId; + requestParams.json.service_id = createServiceId; + if (typeof next === 'function') { + next(); + } + } catch (error) { + if (typeof next === 'function') { + next(error); + return; + } + throw error; + } +} + +async function setUpdateAppointmentPayload(requestParams, context, ee, next) { + try { + await applyAuthHeader(requestParams); + const { officeId, updateServiceId } = getLoadTestConfig(); + requestParams.json = requestParams.json || {}; + requestParams.json.office_id = officeId; + requestParams.json.service_id = updateServiceId; + if (typeof next === 'function') { + next(); + } + } catch (error) { + if (typeof next === 'function') { + next(error); + return; + } + throw error; + } } // Main / script start (async () => { // Only execute if script is called directly, not if imported. - if (require.main === module){ - if (process.argv.includes('--get-keycloak-token')){ - const {access_token} = await loginToKeycloak(process.env.KEYCLOAK_USERNAME, process.env.KEYCLOAK_PASSWORD); + if (require.main === module) { + if (process.argv.includes('--get-keycloak-token')) { + const { username, password } = getKeycloakCredentials(); + const { access_token } = await loginToKeycloak(username, password); // Log access token to STDOUT, so it can be used as environment variable. - console.log( access_token ) + console.log(access_token); return access_token; } } })(); module.exports = { - setAuthHeader -} + setAuthHeader, + setDraftPayload, + setCreateAppointmentPayload, + setUpdateAppointmentPayload, + getDraftAppointmentPayload, + buildDraftPayloadFromSlot, + findFirstAvailableSlot, + parseSlotDate, +}; diff --git a/tests/loadtesting/package-lock.json b/tests/loadtesting/package-lock.json new file mode 100644 index 000000000..84bfaf251 --- /dev/null +++ b/tests/loadtesting/package-lock.json @@ -0,0 +1,14245 @@ +{ + "name": "queue-management-loadtesting", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "queue-management-loadtesting", + "version": "1.0.0", + "devDependencies": { + "artillery": "^2.0.0-5", + "node-fetch": "^2.6.1" + } + }, + "node_modules/@alcalzone/ansi-tokenize": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz", + "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=14.13.1" + } + }, + "node_modules/@alcalzone/ansi-tokenize/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@alcalzone/ansi-tokenize/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@artilleryio/int-commons": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@artilleryio/int-commons/-/int-commons-2.12.0.tgz", + "integrity": "sha512-2DVvB0txMlu7iCiipbHapif81K/8HKzGFR+ezkICzwWGotyzp9QcZXMmyfJgmgsWkVUWGOWX48ZTLmSINH+bQw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "async": "^2.6.4", + "cheerio": "^1.0.0-rc.10", + "debug": "^4.3.1", + "deep-for-each": "^3.0.0", + "espree": "^9.4.1", + "jsonpath-plus": "^10.0.0", + "lodash": "^4.17.19", + "ms": "^2.1.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@artilleryio/int-core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@artilleryio/int-core/-/int-core-2.16.0.tgz", + "integrity": "sha512-dWtwjg33TpP7DIVuRHstRCZm4o3iNFg+qSxL1FrJ70DreK1cDFn4fNv9t3iOsoQ/dEnQtoybuTF5m2Cbq+9ssA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@artilleryio/int-commons": "2.12.0", + "@artilleryio/sketches-js": "^2.1.1", + "agentkeepalive": "^4.1.0", + "arrivals": "^2.1.2", + "async": "^2.6.4", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.10", + "cookie-parser": "^1.4.3", + "csv-parse": "^4.16.3", + "debug": "^4.3.1", + "decompress-response": "^6.0.0", + "deep-for-each": "^3.0.0", + "driftless": "^2.0.3", + "esprima": "^4.0.0", + "eventemitter3": "^4.0.4", + "fast-deep-equal": "^3.1.3", + "filtrex": "^0.5.4", + "form-data": "^3.0.0", + "got": "^11.8.5", + "hpagent": "^0.1.1", + "https-proxy-agent": "^5.0.0", + "lodash": "^4.17.19", + "ms": "^2.1.3", + "protobufjs": "^7.2.4", + "socket.io-client": "^4.5.1", + "socketio-wildcard": "^2.0.0", + "tough-cookie": "^5.0.0-rc.2", + "uuid": "^8.0.0", + "ws": "^7.5.7" + } + }, + "node_modules/@artilleryio/sketches-js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@artilleryio/sketches-js/-/sketches-js-2.1.1.tgz", + "integrity": "sha512-H3D50vDb37E3NGYXY0eUFAm5++moElaqoAu0MWYZhgzaA3IT2E67bRCL8U4LKHuVf/MgDZk14uawIjc4WVjOUQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch": { + "version": "3.1036.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch/-/client-cloudwatch-3.1036.0.tgz", + "integrity": "sha512-5TNbjNLvbFBVnO4raVC3SsE39O1eshpamibAvhoy+tHAdmHV29OP7yeIunbf/ulQebYELHjv4p+/ZqnlZ27ebA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/credential-provider-node": "^3.972.36", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.21", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-compression": "^4.3.46", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.5", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.16", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.1036.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1036.0.tgz", + "integrity": "sha512-MQjdqPph5ZwaG6bGHeEr480NLFskTdr+ZEqXOoiBlwJUCy1sXHT3S/xrUAIVvGX93OetjOMbC81BHxNUHd6TkA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/credential-provider-node": "^3.972.36", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.21", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.5", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.974.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.5.tgz", + "integrity": "sha512-lMPlYlYfQdNZhlkJgnkmESwrY+hNh3PljmZ+37oAqLNdJ6rnILAwFSyc6B3bJeDOtMORNnMQIej0aTRuOlDyhQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/xml-builder": "^3.972.19", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.28.tgz", + "integrity": "sha512-UXhc4FfxbfNaIqycDnIZ+W8CMAoCtcJJfZkq+cWSUwQRN0V0d0uAoN2qCFyKZip8inlHeKJmNQsPliKKcElP8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.31.tgz", + "integrity": "sha512-X/yGB73LmDW/6MdDJGCDzZBUXnM3ys4vs9l+5ZTJmiEswDdP1OjeoAFlFjVGS9o4KB2wZWQ9KOfdVNSSK6Ep3w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.33.tgz", + "integrity": "sha512-c0ZF+lwoWVvX5iCaGKL5T/4DnIw88CGqxA0BcBs3U86mIp5EZYPVg+KSPkMXOyokmADvNewiMUfSG2uFwjRp0g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.35.tgz", + "integrity": "sha512-jsU4u/cRkKFLKQS0k918FQ27fzXLG5ENiLWQMYE6581zLeI2hWh04ptlrvZMB3wJT/5d+vSzJk74X1CMFr4y8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/credential-provider-env": "^3.972.31", + "@aws-sdk/credential-provider-http": "^3.972.33", + "@aws-sdk/credential-provider-login": "^3.972.35", + "@aws-sdk/credential-provider-process": "^3.972.31", + "@aws-sdk/credential-provider-sso": "^3.972.35", + "@aws-sdk/credential-provider-web-identity": "^3.972.35", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.35.tgz", + "integrity": "sha512-5oa3j0cA50jPqgNhZ9XdJVopuzUf1klRb28/2MfLYWWiPi9DRVvbrBWT+DidbHTT36520VuXZJahQwR+YgSjrg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.36", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.36.tgz", + "integrity": "sha512-4nT2T8Z7vH8KE9EdjEsuIlHpZSlcaK2PrKbQBjuUGU46BCCzF3WvP0u0Uiosni3Ykmmn4rWLVawoOCLotUtCbg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.31", + "@aws-sdk/credential-provider-http": "^3.972.33", + "@aws-sdk/credential-provider-ini": "^3.972.35", + "@aws-sdk/credential-provider-process": "^3.972.31", + "@aws-sdk/credential-provider-sso": "^3.972.35", + "@aws-sdk/credential-provider-web-identity": "^3.972.35", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.31.tgz", + "integrity": "sha512-eKeT4MXumpBJsrDLCYcSzIkFPVTFn/es7It2oogp2OhU/ic7P/+xzFpQx9ZhwtXS57Mc5S42BPWi7lHmvs/nYg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.35.tgz", + "integrity": "sha512-bCuBdfnj0KGDMdLp6utMTLiJcFN2ek9EgZinxQZZSc3FxjJ/HSqeqab2cjbnoNfy8RM6suDCsRkmVY1izp9I+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/token-providers": "3.1036.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.35.tgz", + "integrity": "sha512-swW6Bwvl8lanyEMtZOWE/oR6yqcRQH4HTQZUVsnDVgoXvRjRywpYpLv2BWwjUFyjPrqsdX6FeTkf4tMSe/qFTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.1036.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1036.0.tgz", + "integrity": "sha512-7ZIp9c9MXhBhTHLsdKluREogxoazjenIUERGmoXj6Y2GtpgCqpUYqk5550sA4BytLE1mDExbUqKWEBY6jvTwmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.1036.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/credential-provider-cognito-identity": "^3.972.28", + "@aws-sdk/credential-provider-env": "^3.972.31", + "@aws-sdk/credential-provider-http": "^3.972.33", + "@aws-sdk/credential-provider-ini": "^3.972.35", + "@aws-sdk/credential-provider-login": "^3.972.35", + "@aws-sdk/credential-provider-node": "^3.972.36", + "@aws-sdk/credential-provider-process": "^3.972.31", + "@aws-sdk/credential-provider-sso": "^3.972.35", + "@aws-sdk/credential-provider-web-identity": "^3.972.35", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.10.tgz", + "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.10.tgz", + "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.11.tgz", + "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.34", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.34.tgz", + "integrity": "sha512-/UL96JKjsjdodcRRMKl99tLQvK6Oi9ptLC9iU1yiTF/ruaDX0mtBBtnLNZDxIZRJOCVOtB49ed1YaTadqygk8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.35.tgz", + "integrity": "sha512-hOFWNOjVmOocpRlrU04nYxjMOeoe0Obu5AXEuhB8zblMCPl3cG1hdluQCZERRKFyhMQjwZnDbhSHjoMUjetFGw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-retry": "^4.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.3.tgz", + "integrity": "sha512-SivE6GP228IVgfsrr2c/vqTg95X0Qj39Yw4uIrcddpkUzIltNMoNOR62leHOLhODfjv9K8X2mPTwS69A5kT0nQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/signature-v4-multi-region": "^3.996.22", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.21", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.5", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz", + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/config-resolver": "^4.4.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.22.tgz", + "integrity": "sha512-/rXhMXteD+BqhFd0nYprAgcZ/KtU+963uftPqd3tiFcFfooHZINXUGtOmo2SQjRVauCTNqIEzkwuSETdZFqTTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.34", + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1036.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1036.0.tgz", + "integrity": "sha512-aNSJ6jjDYayxN9ZA1JpycVScX93Lx03kKZ1EXt3DGOTahcWVLJj3oLAlop0xKP+vP2Ga2t49p1tEaMkTbCCaZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz", + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-endpoints": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.10.tgz", + "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.21.tgz", + "integrity": "sha512-Av4UHTcAWgdvbN0IP9pbtf4Qa1+6LtJqQdZWj5pLn5J67w0pnJJAZZ+7JPPcj2KN3378zD2JDM9DwJKEyvyMTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/types": "^3.973.8", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.19.tgz", + "integrity": "sha512-Cw8IOMdBUEIl8ZlhRC3Dc/E64D5B5/8JhV6vhPLiPfJwcRC84S6F8aBOIi/N4vR9ZyA4I5Cc0Ateb/9EHaJXeQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "fast-xml-parser": "5.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/arm-containerinstance": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@azure/arm-containerinstance/-/arm-containerinstance-9.1.0.tgz", + "integrity": "sha512-N9T3/HJwWXvJuz7tin+nO+DYYCTGHILJ5Die3TtdF8Wd1ITfXGqB0vY/wOnspUu/AGojhaIKGmawAfPdw2kX8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.7.0", + "@azure/core-lro": "^2.5.0", + "@azure/core-paging": "^1.2.0", + "@azure/core-rest-pipeline": "^1.8.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.4.0.tgz", + "integrity": "sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" + } + }, + "node_modules/@azure/core-http-compat/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-xml": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.5.1.tgz", + "integrity": "sha512-xcNRHqCoSp4AunOALEae6A8f3qATb83gSrm31Iqb01OzblvC3/W/bfXozcq78EzIdzZzuH1bZ2NvRR0TdX709w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^5.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.8.0.tgz", + "integrity": "sha512-X7IZV77bN56l7sbLjkcbQJX1t3U4tgxqztDr/XFbUcUfKk+z2FavcLgKP+OYUNj0wl/pEEtV9lldW9siY8BuHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.5.1.tgz", + "integrity": "sha512-WS9w9SfI8SEYO7mTnxGeZ3UwQfhAVYCWglYF2/7GNx3ioHiAs2gPkl9eSwVs8cPrmiGh+zi9ai/OOKoq4cyzDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.4.tgz", + "integrity": "sha512-G4LXGGggok1QC48uKu64/SV2DPRDlddmV8EieK8pflsNYMj9/Zz+Y9OHoEBhT15h+zpdwXXLYA/7PJCR/yZ8aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.5.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.31.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.31.0.tgz", + "integrity": "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.5", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.3.0", + "events": "^3.0.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-blob/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/storage-common": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.3.0.tgz", + "integrity": "sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.1.4", + "events": "^3.3.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-common/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/storage-queue": { + "version": "12.29.0", + "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.29.0.tgz", + "integrity": "sha512-p02H+TbPQWSI/SQ4CG+luoDvpenM+4837NARmOE4oPNOR5vAq7qRyeX72ffyYL2YLnkcyxETh28/bp/TiVIM+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.0.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.3", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.2.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-queue/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@base2/pretty-print-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz", + "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dependents/detective-less": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-4.1.0.tgz", + "integrity": "sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^6.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@inquirer/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/ts-node-temp-fork-for-pr-2009": { + "version": "10.9.7", + "resolved": "https://registry.npmjs.org/@isaacs/ts-node-temp-fork-for-pr-2009/-/ts-node-temp-fork-for-pr-2009-10.9.7.tgz", + "integrity": "sha512-9f0bhUr9TnwwpgUhEpr3FjxSaH/OHaARkE2F9fM0lS4nIs2GNerrvGwQz493dk0JKlTaGYVrKbq36vA/whZ34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node14": "*", + "@tsconfig/node16": "*", + "@tsconfig/node18": "*", + "@tsconfig/node20": "*", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=4.2" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/@isaacs/ts-node-temp-fork-for-pr-2009/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@ngneat/falso": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@ngneat/falso/-/falso-7.4.0.tgz", + "integrity": "sha512-7MzPP0YGNHDrohf/epmz6SVIjHGhKyHbh0bm+iZ1z/7KVW4xZi9Dx6Tl9NMPy6a4lWh/t3WXSsCGkgkuJ/eroQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "seedrandom": "3.0.5", + "uuid": "8.3.2" + } + }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-1.1.0.tgz", + "integrity": "sha512-PfnWuOkQgu7gCbnSsAisaX7hKOdZ4wSAhAzH3/ph5dSGau52kCRrMMGbiSQLwyTZpgldkZ49b0brkOr1AzGBHQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", + "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@oclif/core": { + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.5.tgz", + "integrity": "sha512-qcdCF7NrdWPfme6Kr34wwljRCXbCVpL1WVxiNy0Ep6vbWKjxAjFQwuhqkoyL0yjI+KdwtLcOCGn5z2yzdijc8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-help": { + "version": "6.2.44", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.44.tgz", + "integrity": "sha512-x03Se2LtlOOlGfTuuubt5C4Z8NHeR4zKXtVnfycuLU+2VOMu2WpsGy9nbs3nYuInuvsIY1BizjVaTjUz060Sig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-not-found": { + "version": "3.2.80", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.80.tgz", + "integrity": "sha512-yTLjWvR1r/Rd/cO2LxHdMCDoL5sQhBYRUcOMCmxZtWVWhx4rAZ8KVUPDVsb+SvjJDV5ADTDBgt1H52fFx7YWqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/prompts": "^7.10.1", + "@oclif/core": "^4.10.5", + "ansis": "^3.17.0", + "fast-levenshtein": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.41.2.tgz", + "integrity": "sha512-JEV2RAqijAFdWeT6HddYymfnkiRu2ASxoTBr4WsnGJhOjWZkEy6vp+Sx9ozr1NaIODOa2HUyckExIqQjn6qywQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.15.2.tgz", + "integrity": "sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.41.2.tgz", + "integrity": "sha512-gQuCcd5QSMkfi1XIriWAoak/vaRvFzpvtzh2hjziIvbnA3VtoGD3bDb2dzEzOA1iSWO0/tHwnBsSmmUZsETyOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/exporter-metrics-otlp-http": "0.41.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-metrics": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz", + "integrity": "sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.41.2.tgz", + "integrity": "sha512-+YeIcL4nuldWE89K8NBLImpXCvih04u1MBnn8EzvoywG2TKR5JC3CZEPepODIxlsfGSgP8W5khCEP1NHZzftYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-metrics": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz", + "integrity": "sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.41.2.tgz", + "integrity": "sha512-OLNs6wF84uhxn8TJ8Bv1q2ltdJqjKA9oUEtICcUDDzXIiztPxZ9ur/4xdMk9T3ZJeFMfrhj8eYDkpETBy+fjCg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/exporter-metrics-otlp-http": "0.41.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-proto-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-metrics": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz", + "integrity": "sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.43.0.tgz", + "integrity": "sha512-h/oofzwyONMcAeBXD6+E6+foFQg9CPadBFcKAGoMIyVSK7iZgtK5DLEwAF4jz5MhfxWNmwZjHXFRc0GqCRx/tA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.17.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.43.0", + "@opentelemetry/otlp-transformer": "0.43.0", + "@opentelemetry/resources": "1.17.0", + "@opentelemetry/sdk-trace-base": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/api-logs": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.43.0.tgz", + "integrity": "sha512-0CXMOYPXgAdLM2OzVkiUfAL6QQwWVhnMfUXCqLsITY42FZ9TxAhZIHkoc4mfVxvPuXsBnRYGR8UQZX86p87z4A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.17.0.tgz", + "integrity": "sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.7.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.43.0.tgz", + "integrity": "sha512-LXNtRFVuPRXB9q0qdvrLikQ3NtT9Jmv255Idryz3RJPhOh/Fa03sBASQoj3D55OH3xazmA90KFHfhJ/d8D8y4A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.43.0.tgz", + "integrity": "sha512-oOpqtDJo9BBa1+nD6ID1qZ55ZdTwEwSSn2idMobw8jmByJKaanVLdr9SJKsn5T9OBqo/c5QY2brMf0TNZkobJQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.17.0", + "@opentelemetry/otlp-exporter-base": "0.43.0", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.43.0.tgz", + "integrity": "sha512-KXYmgzWdVBOD5NvPmGW1nEMJjyQ8gK3N8r6pi4HvmEhTp0v4T13qDSax4q0HfsqmbPJR355oqQSJUnu1dHNutw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.43.0", + "@opentelemetry/core": "1.17.0", + "@opentelemetry/resources": "1.17.0", + "@opentelemetry/sdk-logs": "0.43.0", + "@opentelemetry/sdk-metrics": "1.17.0", + "@opentelemetry/sdk-trace-base": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.7.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.17.0.tgz", + "integrity": "sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.17.0", + "@opentelemetry/semantic-conventions": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.7.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-logs": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.43.0.tgz", + "integrity": "sha512-JyJ2BBRKm37Mc4cSEhFmsMl5ASQn1dkGhEWzAAMSlhPtLRTv5PfvJwhR+Mboaic/eDLAlciwsgijq8IFlf6IgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.17.0", + "@opentelemetry/resources": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.7.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz", + "integrity": "sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.17.0", + "@opentelemetry/resources": "1.17.0", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.7.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.0.tgz", + "integrity": "sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.17.0", + "@opentelemetry/resources": "1.17.0", + "@opentelemetry/semantic-conventions": "1.17.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.7.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.17.0.tgz", + "integrity": "sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.41.2.tgz", + "integrity": "sha512-Y0fGLipjZXLMelWtlS1/MDtrPxf25oM408KukRdkN31a1MEFo4h/ZkNwS7ZfmqHGUa+4rWRt2bi6JBiqy7Ytgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz", + "integrity": "sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.41.2.tgz", + "integrity": "sha512-IGZga9IIckqYE3IpRE9FO9G5umabObIrChlXUHYpMJtDgx797dsb3qXCvLeuAwB+HoB8NsEZstlzmLnoa6/HmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-proto-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz", + "integrity": "sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.30.1.tgz", + "integrity": "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.41.2.tgz", + "integrity": "sha512-pfwa6d+Dax3itZcGWiA0AoXeVaCuZbbqUTsCtOysd2re8C2PWXNxDONUfBWsn+KgxAdi+ljwTjJGiaVLDaIEvQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.41.2.tgz", + "integrity": "sha512-OErK8dYjXG01XIMIpmOV2SzL9ctkZ0Nyhf2UumICOAKtgLvR5dG1JMlsNVp8Jn0RzpsKc6Urv7JpP69wzRXN+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-proto-exporter-base": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.41.2.tgz", + "integrity": "sha512-BxmEMiP6tHiFroe5/dTt9BsxCci7BTLtF7A6d4DKHLiLweWWZxQ9l7hON7qt/IhpKrQcAFD1OzZ1Gq2ZkNzhCw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.41.2.tgz", + "integrity": "sha512-jJbPwB0tNu2v+Xi0c/v/R3YBLJKLonw1p+v3RVjT2VfzeUyzSp/tBeVdY7RZtL6dzZpA9XSmp8UEfWIFQo33yA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.41.2", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-logs": "0.41.2", + "@opentelemetry/sdk-metrics": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz", + "integrity": "sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz", + "integrity": "sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.41.2.tgz", + "integrity": "sha512-smqKIw0tTW15waj7BAPHFomii5c3aHnSE4LQYTszGoK5P9nZs8tEAIpu15UBxi3aG31ZfsLmm4EUQkjckdlFrw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.5.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", + "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/browser-chromium": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.59.1.tgz", + "integrity": "sha512-XDwr0qOrzLXAuBAzg4WO/xctVMb+ldJ54yz9KCpFu8G8MVJzUVFO6BvK1tBtBl4DIoFcoFRKHgUGZT+8wOC8BQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz", + "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.17", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz", + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.17", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.17.tgz", + "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.14.tgz", + "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.17", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.17.tgz", + "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.14.tgz", + "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.14.tgz", + "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-compression": { + "version": "4.3.46", + "resolved": "https://registry.npmjs.org/@smithy/middleware-compression/-/middleware-compression-4.3.46.tgz", + "integrity": "sha512-9f4AZ5dKqKRmO49MPhOoxFoQBLfBgxE9YKG8bQ6lsW9xk+Bn8rkfGlpW8OYlvhuarN+8mja9PjhEudFiR8wGFQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-utf8": "^4.2.2", + "fflate": "0.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.14.tgz", + "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.32", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.32.tgz", + "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-middleware": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.5.tgz", + "integrity": "sha512-wnYOpB5vATFKWrY2Z9Alb0KhjZI6AbzU6Fbz3Hq2GnURdRYWB4q+qWivQtSTwXcmWUA3MZ6krfwL6Cq5MAbxsA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/service-error-classification": "^4.3.0", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.20.tgz", + "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.14.tgz", + "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.14.tgz", + "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.6.1.tgz", + "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.14.tgz", + "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.14.tgz", + "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.14.tgz", + "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.14.tgz", + "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.0.tgz", + "integrity": "sha512-9jKsBYQRPR0xBLgc2415RsA5PIcP2sis4oBdN9s0D13cg1B1284mNTjx9Yc+BEERXzuPm5ObktI96OxsKh8E9A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.9.tgz", + "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.14.tgz", + "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.13", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.13.tgz", + "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz", + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.14.tgz", + "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.49", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.49.tgz", + "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.54", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.54.tgz", + "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.17", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz", + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.14.tgz", + "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.4.tgz", + "integrity": "sha512-FY1UQQ1VFmMwiYp1GVS4MeaGD5O0blLNYK0xCRHU+mJgeoH/hSY8Ld8sJWKQ6uznkh14HveRGQJncgPyNl9J+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.3.0", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.25", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.25.tgz", + "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.16.tgz", + "integrity": "sha512-GtclrKoZ3Lt7jPQ7aTIYKfjY92OgceScftVnkTsG8e1KV8rkvZgN+ny6YSRhd9hxB8rZtwVbmln7NTvE5O3GmQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tapjs/after": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/@tapjs/after/-/after-1.1.31.tgz", + "integrity": "sha512-531NkYOls9PvqfnLsEDRzIWwjynoFRbUVq7pTYuA3PRIw4Ka7jA9uUjILeUurcWjaHrQNzUua0jj/Yu94f6YYw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "is-actual-promise": "^1.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/after-each": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/after-each/-/after-each-2.0.8.tgz", + "integrity": "sha512-btkpQ/BhmRyG50rezduxEZb3pMJblECvTQa41+U2ln2te1prDTlllHlpq4lOjceUksl8KFF1avDqcBqIqPzneQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "function-loop": "^4.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/asserts": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/asserts/-/asserts-2.0.8.tgz", + "integrity": "sha512-57VrI0p2kAqfgHHUwowDvd31eTfDHw3HO4FSSVUCvngPGWa96R6eH9gXa9fNig4qIp4Dup+nI7gJlJfU0R80SA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/stack": "2.0.1", + "is-actual-promise": "^1.0.1", + "tcompare": "7.0.1", + "trivial-deferred": "^2.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/before": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/before/-/before-2.0.8.tgz", + "integrity": "sha512-22ZdGSn/zOKf8J8cb3yfw5R4I/ozdHEDKL8lBWon/zsxxMMvaRTgOtFXEjb4RE+5SDrqQ4NM7ZRYPGhE7T97dw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "is-actual-promise": "^1.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/before-each": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/before-each/-/before-each-2.0.8.tgz", + "integrity": "sha512-Xjgk8/fuP7iFa5CYjFDl05p5PZGRe//VyHJNuYNzWpF1K9PNMtVdlmwplfpFmbrNrw/bIPq7R6LuiPmTBgzuOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "function-loop": "^4.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/chdir": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@tapjs/chdir/-/chdir-1.1.4.tgz", + "integrity": "sha512-axXkT5kWp2/X8l6inKyrqzUhqgvsgrWI8/0xLAdmirpFZ8H6gFxrl763Ozdm27EAmkLnnnWgFITPqUQCuB/tMA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/config": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@tapjs/config/-/config-3.1.6.tgz", + "integrity": "sha512-5gkDMSLXL5798bbCdX4RdLpB4OUQeu9TXftzKmL1+1T2xbcd4q7zfDnCfOB9zTk50x2f04+4h6Q7Z1NcSKIspg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/core": "2.1.6", + "@tapjs/test": "2.2.4", + "chalk": "^5.2.0", + "jackspeak": "^3.1.2", + "polite-json": "^4.0.1", + "tap-yaml": "2.2.2", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6", + "@tapjs/test": "2.2.4" + } + }, + "node_modules/@tapjs/config/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@tapjs/core": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@tapjs/core/-/core-2.1.6.tgz", + "integrity": "sha512-NYMp0bl52DxXfcLmivMKvOIE14aaB9qJjdHeUbs6GZ9yxgD5w0yeiOT+gWEL+1PzZgGWRxSFEpghID1YfXAc4w==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/processinfo": "^3.1.8", + "@tapjs/stack": "2.0.1", + "@tapjs/test": "2.2.4", + "async-hook-domain": "^4.0.1", + "diff": "^5.2.0", + "is-actual-promise": "^1.0.1", + "minipass": "^7.0.4", + "signal-exit": "4.1", + "tap-parser": "16.0.1", + "tap-yaml": "2.2.2", + "tcompare": "7.0.1", + "trivial-deferred": "^2.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + } + }, + "node_modules/@tapjs/error-serdes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tapjs/error-serdes/-/error-serdes-2.0.1.tgz", + "integrity": "sha512-P+M4rtcfkDsUveKKmoRNF+07xpbPnRY5KrstIUOnyn483clQ7BJhsnWr162yYNCsyOj4zEfZmAJI1f8Bi7h/ZA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/filter": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/filter/-/filter-2.0.8.tgz", + "integrity": "sha512-/ps6nOS3CTh1WLfCjJnU7tS4PH4KFgEasFSVPCIFN+BasyoqDapzj4JKIlzQvppZOGTQadKH3wUakafZl7uz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/fixture": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/fixture/-/fixture-2.0.8.tgz", + "integrity": "sha512-LJnjeAMSozPFXzu+wQw2HJsjA9djHbTcyeMnsgiRL/Q8ffcLqAawV3SN6XKdDLdWYUg3e1fXhHspnbsouZj+xA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "mkdirp": "^3.0.0", + "rimraf": "^5.0.5" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/intercept": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/intercept/-/intercept-2.0.8.tgz", + "integrity": "sha512-OF2Q35jtZ20bwV4hRNoca7vqIrzPFR3JR25G2rGru+fgPmq4heN0RLoh0d1O34AbrtXqra2lXkacMB/DPgb01A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/after": "1.1.31", + "@tapjs/stack": "2.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/mock": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@tapjs/mock/-/mock-2.1.6.tgz", + "integrity": "sha512-bNXKrjg/r+i/gfKij5Oo/5Md2DvGNHPSRCHQmjz3VQjpyxqK7S1FGcR0kyqJ8Nof6Wc8yIhpNOCuibj19200IQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/after": "1.1.31", + "@tapjs/stack": "2.0.1", + "resolve-import": "^1.4.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/node-serialize": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/node-serialize/-/node-serialize-2.0.8.tgz", + "integrity": "sha512-92oqhkmIz5wr0yRs1CPQfim5JSwHPSmoDWnQmJlYUZsY1OYgYouQm3ifnPkqK/9hJpVYzlZEQmefxehxbs2WNQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/error-serdes": "2.0.1", + "@tapjs/stack": "2.0.1", + "tap-parser": "16.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/processinfo": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@tapjs/processinfo/-/processinfo-3.1.9.tgz", + "integrity": "sha512-yIbYH9ROI5m5F2B5Hpk6t89OkHBrDbL3qncPO9OfPuSvJsvAIDG91I0hxGQNohdaxmqz5L4QiIYc5Y0KmtLzCQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "node-options-to-argv": "^1.0.0", + "pirates": "^4.0.5", + "process-on-spawn": "^1.0.0", + "signal-exit": "^4.0.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=16.17" + } + }, + "node_modules/@tapjs/reporter": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/reporter/-/reporter-2.0.8.tgz", + "integrity": "sha512-tZn5ZHIrFwjbi59djtdXHBwgSIZSBXdJpz2i9CZ9HEC1nFhWtIr2Jczvrz4ScfixUgA0GNFirz+q+9iA4IFMvw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/config": "3.1.6", + "@tapjs/stack": "2.0.1", + "chalk": "^5.2.0", + "ink": "^4.4.1", + "minipass": "^7.0.4", + "ms": "^2.1.3", + "patch-console": "^2.0.0", + "prismjs-terminal": "^1.2.3", + "react": "^18.2.0", + "string-length": "^6.0.0", + "tap-parser": "16.0.1", + "tap-yaml": "2.2.2", + "tcompare": "7.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/reporter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@tapjs/run": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@tapjs/run/-/run-2.1.7.tgz", + "integrity": "sha512-Hk41E68f1x4eLBm6Rrxx4ARzZzrjwaLbKThb16+f3bGYiajmqAvBdeyNEoQpEWmW+Sv2HSlueOk2SS2P4fyetg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/after": "1.1.31", + "@tapjs/before": "2.0.8", + "@tapjs/config": "3.1.6", + "@tapjs/processinfo": "^3.1.8", + "@tapjs/reporter": "2.0.8", + "@tapjs/spawn": "2.0.8", + "@tapjs/stdin": "2.0.8", + "@tapjs/test": "2.2.4", + "c8": "^9.1.0", + "chalk": "^5.3.0", + "chokidar": "^3.6.0", + "foreground-child": "^3.1.1", + "glob": "^10.3.16", + "minipass": "^7.0.4", + "mkdirp": "^3.0.1", + "opener": "^1.5.2", + "pacote": "^17.0.6", + "resolve-import": "^1.4.5", + "rimraf": "^5.0.5", + "semver": "^7.6.0", + "signal-exit": "^4.1.0", + "tap-parser": "16.0.1", + "tap-yaml": "2.2.2", + "tcompare": "7.0.1", + "trivial-deferred": "^2.0.0", + "which": "^4.0.0" + }, + "bin": { + "tap-run": "dist/esm/index.js" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/run/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tapjs/run/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tapjs/run/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@tapjs/run/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/run/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tapjs/run/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/run/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tapjs/snapshot": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/snapshot/-/snapshot-2.0.8.tgz", + "integrity": "sha512-L0vtqWKkgnQt/XNQkvHOme9Np7ffteCNf1P0F9mz2YiJion4er1nv6pZuJoKVxXFQsbNd2k+LGyx0Iw+bIzwFg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "is-actual-promise": "^1.0.1", + "tcompare": "7.0.1", + "trivial-deferred": "^2.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/spawn": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/spawn/-/spawn-2.0.8.tgz", + "integrity": "sha512-vCYwynIYJNijY87uHFANe+gCu9rdGoe4GOBmghl6kwDy7eISmcN/FW5TlmrjePMNhTvrDMeYqOIAzqh3WRYmPA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/stack": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tapjs/stack/-/stack-2.0.1.tgz", + "integrity": "sha512-3rKbZkRkLeJl9ilV/6b80YfI4C4+OYf7iEz5/d0MIVhmVvxv0ttIy5JnZutAc4Gy9eRp5Ne5UTAIFOVY5k36cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/stdin": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/stdin/-/stdin-2.0.8.tgz", + "integrity": "sha512-tW/exLXuDqjtH2wjptiPHXBahkdSyoppxDY56l9MG4tiz66dMN6NTCZFvQxp7+3t+lsQKqJp/74z8T/ayp+vZA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/test": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tapjs/test/-/test-2.2.4.tgz", + "integrity": "sha512-QIgq2BhMpwO9SN8I0qlwZYXAllO4xWCfJ0MgAGhc+J7p69B5p9dDNPmyOreHeXWMmk6VlNj3oWveoXb5Zn9xZQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/ts-node-temp-fork-for-pr-2009": "^10.9.7", + "@tapjs/after": "1.1.31", + "@tapjs/after-each": "2.0.8", + "@tapjs/asserts": "2.0.8", + "@tapjs/before": "2.0.8", + "@tapjs/before-each": "2.0.8", + "@tapjs/chdir": "1.1.4", + "@tapjs/filter": "2.0.8", + "@tapjs/fixture": "2.0.8", + "@tapjs/intercept": "2.0.8", + "@tapjs/mock": "2.1.6", + "@tapjs/node-serialize": "2.0.8", + "@tapjs/snapshot": "2.0.8", + "@tapjs/spawn": "2.0.8", + "@tapjs/stdin": "2.0.8", + "@tapjs/typescript": "1.4.13", + "@tapjs/worker": "2.0.8", + "glob": "^10.3.16", + "jackspeak": "^3.1.2", + "mkdirp": "^3.0.0", + "package-json-from-dist": "^1.0.0", + "resolve-import": "^1.4.5", + "rimraf": "^5.0.5", + "sync-content": "^1.0.1", + "tap-parser": "16.0.1", + "tshy": "^1.14.0", + "typescript": "5.4", + "walk-up-path": "^3.0.1" + }, + "bin": { + "generate-tap-test-class": "dist/esm/build.mjs" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/test/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tapjs/test/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tapjs/test/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/test/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tapjs/test/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@tapjs/typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@tapjs/typescript/-/typescript-1.4.13.tgz", + "integrity": "sha512-MNs7zlhM6G3pNUIjkKXDxgNCwCGZt2bUCGtVunSTDVIrKiUlHAl4QSjQ1oTjumHlCi9gFIWiwFAvpHekzFti0w==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/ts-node-temp-fork-for-pr-2009": "^10.9.7" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tapjs/worker": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@tapjs/worker/-/worker-2.0.8.tgz", + "integrity": "sha512-AySf2kV6OHvwgD3DrLdT2az2g4hRdoRtKsFCLdZo3jOoKte+ft/IQJEnOW7CPT0RYUskS3elv6eabYgSyTH4tg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "peerDependencies": { + "@tapjs/core": "2.1.6" + } + }, + "node_modules/@tsconfig/node14": { + "version": "14.1.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.8.tgz", + "integrity": "sha512-SjGT+qPvh8Uhc849yNMD0ZIPr69AyB7Z46nMqhrI3gCVocd6mhI0jP4YE4onO/ufpmengRfTxNMpdpKEp2xRIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "16.1.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-16.1.8.tgz", + "integrity": "sha512-T/CfdwFry660WjZor56z0F3pxeCllt8KOxWcHFW6ZEuULKUObTDEMdgtctyuJPxwqyWDsvHRfxHaJ4FIICyoqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node18": { + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.6.tgz", + "integrity": "sha512-eAWQzAjPj18tKnDzmWstz4OyWewLUNBm9tdoN9LayzoboRktYx3Enk1ZXPmThj55L7c4VWYq/Bzq0A51znZfhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.9.tgz", + "integrity": "sha512-IjlTv1RsvnPtUcjTqtVsZExKVq+KQx4g5pCP5tI7rAs6Xesl2qFwSz/tPDBC4JajkL/MlezBu3gPUwqRHl+RIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", + "integrity": "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/archiver/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arrivals": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arrivals/-/arrivals-2.1.2.tgz", + "integrity": "sha512-g3+rxhxUen2H4+PPBOz6U6pkQ4esBuQPna1rPskgK1jamBdDZeoppyB2vPUM/l0ccunwRrq4r2rKgCvc2FnrFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "debug": "^4.0.1", + "nanotimer": "0.3.14" + } + }, + "node_modules/artillery": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/artillery/-/artillery-2.0.21.tgz", + "integrity": "sha512-4viCw0LQnuc9SyGiBlMbvdfGbNZxQao3twRmyEqF7aIiWx4Vfn8ag3uVxo8tNUzInf6sMcTESm0RSUe9jl768w==", + "deprecated": "This version is no longer supported", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@artilleryio/int-commons": "2.12.0", + "@artilleryio/int-core": "2.16.0", + "@aws-sdk/credential-providers": "^3.127.0", + "@azure/arm-containerinstance": "^9.1.0", + "@azure/identity": "^4.2.0", + "@azure/storage-blob": "^12.18.0", + "@azure/storage-queue": "^12.22.0", + "@oclif/core": "^4.0.25", + "@oclif/plugin-help": "^6.2.13", + "@oclif/plugin-not-found": "^3.2.22", + "archiver": "^5.3.1", + "artillery-engine-playwright": "1.18.0", + "artillery-plugin-apdex": "1.12.0", + "artillery-plugin-ensure": "1.15.0", + "artillery-plugin-expect": "2.15.0", + "artillery-plugin-fake-data": "1.12.0", + "artillery-plugin-metrics-by-endpoint": "1.15.0", + "artillery-plugin-publish-metrics": "2.26.0", + "artillery-plugin-slack": "1.10.0", + "async": "^2.6.4", + "aws-sdk": "^2.1338.0", + "chalk": "^2.4.2", + "chokidar": "^3.6.0", + "ci-info": "^4.0.0", + "cli-table3": "^0.6.0", + "cross-spawn": "^7.0.3", + "csv-parse": "^4.16.3", + "debug": "^4.3.1", + "dependency-tree": "^10.0.9", + "detective-es6": "^4.0.1", + "dotenv": "^16.0.1", + "driftless": "^2.0.3", + "esbuild-wasm": "^0.19.8", + "eventemitter3": "^4.0.4", + "fs-extra": "^10.1.0", + "got": "^11.8.5", + "joi": "^17.6.0", + "js-yaml": "^3.13.1", + "jsonwebtoken": "^9.0.1", + "lodash": "^4.17.19", + "moment": "^2.29.4", + "nanoid": "^3.3.4", + "ora": "^4.0.4", + "posthog-node": "^2.2.3", + "rc": "^1.2.8", + "sqs-consumer": "5.8.0", + "temp": "^0.9.4", + "tmp": "0.2.1", + "walk-sync": "^0.2.3", + "yaml-js": "^0.2.3" + }, + "bin": { + "artillery": "bin/run" + }, + "engines": { + "node": ">= 18.16.1" + } + }, + "node_modules/artillery-engine-playwright": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/artillery-engine-playwright/-/artillery-engine-playwright-1.18.0.tgz", + "integrity": "sha512-+BJhiiJIiCC+mtXuxdvmKNb8Kj+NXXsZs75SeLILukytg3ZMRhEWFbL7FnfizhzGqh/b2WGvOOjQYUV/OkRt9w==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@playwright/browser-chromium": "^1.48.0", + "@playwright/test": "^1.48.0", + "debug": "^4.3.2", + "playwright": "^1.48.0" + } + }, + "node_modules/artillery-plugin-apdex": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-apdex/-/artillery-plugin-apdex-1.12.0.tgz", + "integrity": "sha512-RFwfXjSY7ELCMSBrLy5MiXt5WhseLDNrwXwOgbHOvR+40ULhtSb0N+EcC3Kxoxcn6MzPX9414RkS0Kj2e6TmIw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "tap": "^19.0.2" + } + }, + "node_modules/artillery-plugin-ensure": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-ensure/-/artillery-plugin-ensure-1.15.0.tgz", + "integrity": "sha512-iUq+eTatH/jSrGy6SF/l50q5R+yMKiRfLh5S4lVJGq6Tf7FLgYNu7JelybJyTVUxUZcG0kyxHGmPcxnM78PAjQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "chalk": "^2.4.2", + "debug": "^4.3.3", + "filtrex": "^2.2.3" + } + }, + "node_modules/artillery-plugin-ensure/node_modules/filtrex": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/filtrex/-/filtrex-2.2.3.tgz", + "integrity": "sha512-TL12R6SckvJdZLibXqyp4D//wXZNyCalVYGqaWwQk9zucq9dRxmrJV4oyuRq4PHFHCeV5ZdzncIc/Ybqv1Lr6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/artillery-plugin-expect": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-expect/-/artillery-plugin-expect-2.15.0.tgz", + "integrity": "sha512-hVo628Y6Z/LiWG3frUl1GIwUqLBOlaiCxepsVkY/kN4KSX0FQV+SyWrGgzIw+eE0KDivWIUfnN93zhain9hotw==", + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.2", + "jmespath": "^0.16.0", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">= 14.17.6" + } + }, + "node_modules/artillery-plugin-expect/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/artillery-plugin-expect/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/artillery-plugin-expect/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/artillery-plugin-expect/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/artillery-plugin-expect/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/artillery-plugin-fake-data": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-fake-data/-/artillery-plugin-fake-data-1.12.0.tgz", + "integrity": "sha512-fsYIQCsJSR9Jw/RFcqUfReHUpDBN4ddnqtaxkuwtpSUfVyqDFuiOIR7QYIsqBFfq1JNSkw2Op47Ol0tNFpZ5fA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@ngneat/falso": "^7.1.1" + } + }, + "node_modules/artillery-plugin-metrics-by-endpoint": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-metrics-by-endpoint/-/artillery-plugin-metrics-by-endpoint-1.15.0.tgz", + "integrity": "sha512-I95bvm4jG67lPYlQwSQZNcKajZee7YtezCVVWBN47tkrFDUDqdaba3dGCI8O+9H9kE5dm0s7WdzyfvUZZCxE+A==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "debug": "^4.3.2" + } + }, + "node_modules/artillery-plugin-publish-metrics": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-publish-metrics/-/artillery-plugin-publish-metrics-2.26.0.tgz", + "integrity": "sha512-qI3S3AgkF2PVkP5POxubehju6onMl3HPq9WTGYK29gp3MaSSf/s+5i2UNjkAEmdrO2fqBnhanSo+Df8ffuC0eA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@aws-sdk/client-cloudwatch": "^3.370.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/context-async-hooks": "^1.17.1", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.41.2", + "@opentelemetry/exporter-metrics-otlp-http": "^0.41.2", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.41.2", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.43.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.41.2", + "@opentelemetry/exporter-trace-otlp-proto": "^0.41.2", + "@opentelemetry/exporter-zipkin": "^1.15.2", + "@opentelemetry/resources": "^1.15.2", + "@opentelemetry/sdk-metrics": "^1.15.2", + "@opentelemetry/sdk-trace-base": "^1.15.2", + "@opentelemetry/semantic-conventions": "^1.15.2", + "async": "^2.6.1", + "datadog-metrics": "^0.9.3", + "debug": "^4.1.1", + "dogapi": "^2.8.4", + "hot-shots": "^6.0.1", + "lightstep-tracer": "^0.31.0", + "mixpanel": "^0.13.0", + "opentracing": "^0.14.5", + "prom-client": "^14.0.1", + "semver": "^7.3.5", + "uuid": "^8.3.2" + } + }, + "node_modules/artillery-plugin-slack": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/artillery-plugin-slack/-/artillery-plugin-slack-1.10.0.tgz", + "integrity": "sha512-3foLJ/fkLeCl5P2tZ43d+hKzikNYvtiWWYaxGaGfKpBlNchKSXmiPh4ZpPur/VzKynnA5IjnG4eagSbf/tZ+Wg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "debug": "^4.3.4", + "got": "^11.8.5" + } + }, + "node_modules/ast-module-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-5.0.0.tgz", + "integrity": "sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-hook-domain": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-4.0.1.tgz", + "integrity": "sha512-bSktexGodAjfHWIrSrrqxqWzf1hWBZBpmPNZv+TYUMyWa2eoefFc6q6H1+KtdHYSz35lrhWdmXt/XK9wNEZvww==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/auto-bind": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", + "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1693.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1693.0.tgz", + "integrity": "sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==", + "deprecated": "The AWS SDK for JavaScript (v2) has reached end-of-support, and no longer receives updates. Please migrate your code to use AWS SDK for JavaScript (v3). More info https://a.co/cUPnyil", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-or-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.3.0.tgz", + "integrity": "sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clean-stack/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csv-parse": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", + "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==", + "dev": true, + "license": "MIT" + }, + "node_modules/datadog-metrics": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/datadog-metrics/-/datadog-metrics-0.9.3.tgz", + "integrity": "sha512-BVsBX2t+4yA3tHs7DnB5H01cHVNiGJ/bHA8y6JppJDyXG7s2DLm6JaozPGpgsgVGd42Is1CHRG/yMDQpt877Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "dogapi": "2.8.4" + } + }, + "node_modules/datadog-metrics/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/datadog-metrics/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-for-each": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/deep-for-each/-/deep-for-each-3.0.0.tgz", + "integrity": "sha512-pPN+0f8jlnNP+z90qqOdxGghJU5XM6oBDhvAR+qdQzjCg5pk/7VPPvKK1GqoXEFkHza6ZS+Otzzvmr0g3VUaKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.isplainobject": "^4.0.6" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dependency-tree": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-10.0.9.tgz", + "integrity": "sha512-dwc59FRIsht+HfnTVM0BCjJaEWxdq2YAvEDy4/Hn6CwS3CBWMtFnL3aZGAkQn3XCYxk/YcTDE4jX2Q7bFTwCjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.1", + "filing-cabinet": "^4.1.6", + "precinct": "^11.0.5", + "typescript": "^5.0.4" + }, + "bin": { + "dependency-tree": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-amd": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-5.0.2.tgz", + "integrity": "sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^5.0.0", + "escodegen": "^2.0.0", + "get-amd-module-type": "^5.0.1", + "node-source-walk": "^6.0.1" + }, + "bin": { + "detective-amd": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-cjs": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-5.0.1.tgz", + "integrity": "sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^5.0.0", + "node-source-walk": "^6.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-es6": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-4.0.1.tgz", + "integrity": "sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-source-walk": "^6.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-postcss": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-6.1.3.tgz", + "integrity": "sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-url": "^1.2.4", + "postcss": "^8.4.23", + "postcss-values-parser": "^6.0.2" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/detective-sass": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-5.0.3.tgz", + "integrity": "sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^6.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-scss": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-4.0.3.tgz", + "integrity": "sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^6.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-stylus": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-4.0.0.tgz", + "integrity": "sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/detective-typescript": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-11.2.0.tgz", + "integrity": "sha512-ARFxjzizOhPqs1fYC/2NMC3N4jrQ6HvVflnXBTRqNEqJuXwyKLRr9CrJwkRcV/SnZt1sNXgsF6FPm0x57Tq0rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "^5.62.0", + "ast-module-types": "^5.0.0", + "node-source-walk": "^6.0.2", + "typescript": "^5.4.4" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dogapi": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/dogapi/-/dogapi-2.8.4.tgz", + "integrity": "sha512-065fsvu5dB0o4+ENtLjZILvXMClDNH/yA9H6L8nsdcNiz9l0Hzpn7aQaCOPYXxqyzq4CRPOdwkFXUjDOXfRGbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.2", + "json-bigint": "^1.0.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rc": "^1.2.8" + }, + "bin": { + "dogapi": "bin/dogapi" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/driftless": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/driftless/-/driftless-2.0.3.tgz", + "integrity": "sha512-hSDKsQphnL4O0XLAiyWQ8EiM9suXH0Qd4gMtwF86b5wygGV8r95w0JcA38FOmx9N3LjFCIHLG2winLPNken4Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "present": "^0.0.3" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", + "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ensure-posix-path": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz", + "integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.19.12.tgz", + "integrity": "sha512-Zmc4hk6FibJZBcTx5/8K/4jT3/oG1vkGTEeKJUQFCUQKimD6Q7+adp/bdVQyYJFolMKaXkQnVZdV4O5ZaTYmyQ==", + "dev": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-to-array": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-2.0.3.tgz", + "integrity": "sha512-f/qE2gImHRa4Cp2y1stEOSgw8wTFyUdVJX7G//bMwbaV9JqISFxg99NbmVQeP7YLnDUZ2un851jlaDrlpmGehQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", + "integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz", + "integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.1.tgz", + "integrity": "sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filing-cabinet": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-4.2.0.tgz", + "integrity": "sha512-YZ21ryzRcyqxpyKggdYSoXx//d3sCJzM3lsYoaeg/FyXdADGJrUl+BW1KIglaVLJN5BBcMtWylkygY8zBp2MrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-module-path": "^2.2.0", + "commander": "^10.0.1", + "enhanced-resolve": "^5.14.1", + "is-relative-path": "^1.0.2", + "module-definition": "^5.0.1", + "module-lookup-amd": "^8.0.5", + "resolve": "^1.22.3", + "resolve-dependency-path": "^3.0.2", + "sass-lookup": "^5.0.1", + "stylus-lookup": "^5.0.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.0.4" + }, + "bin": { + "filing-cabinet": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filtrex": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/filtrex/-/filtrex-0.5.4.tgz", + "integrity": "sha512-2phGAjWOYRf96Al6s+w/hMjObP1cRyQ95hoZApjeFO75DXN4Flh9uuUAtL3LI4fkryLa2QWdA8MArvt0GMU0pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-loop": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-4.0.0.tgz", + "integrity": "sha512-f34iQBedYF3XcI93uewZZOnyscDragxgTK/eTvVB74k3fCD0ZorOi5BV9GS4M8rz/JoNi0Kl3qX5Y9MH3S/CLQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-amd-module-type": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-5.0.1.tgz", + "integrity": "sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^5.0.0", + "node-source-walk": "^6.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "gonzales": "bin/gonzales.js" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/google-protobuf": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.6.1.tgz", + "integrity": "sha512-SJYemeX5GjDLPnadcmCNQePQHCS4Hl5fOcI/JawqDIYFhCmrtYAjcx/oTQx/Wi8UuCuZQhfvftbmPePPAYHFtA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hex2dec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.0.1.tgz", + "integrity": "sha512-F9QO0+ZI8r1VZudxw21bD/U5pb2Y9LZY3TsnVqCPaijvw5mIhH5jsH29acLPijl5fECfD8FetJtgX8GN5YPM9Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hot-shots": { + "version": "6.8.7", + "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-6.8.7.tgz", + "integrity": "sha512-XH8iezBSZgVw2jegu96pUfF1Zv0VZ/iXjb7L5yE3F7mn7/bdhf4qeniXjO0wQWeefe433rhOsazNKLxM+XMI9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "optionalDependencies": { + "unix-dgram": "2.0.x" + } + }, + "node_modules/hpagent": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-0.1.2.tgz", + "integrity": "sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/ink": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/ink/-/ink-4.4.1.tgz", + "integrity": "sha512-rXckvqPBB0Krifk5rn/5LvQGmyXwCUpBfmTwbkQNBY9JY8RSl3b8OftBNEYxg4+SWUhEKcPifgope28uL9inlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alcalzone/ansi-tokenize": "^0.1.3", + "ansi-escapes": "^6.0.0", + "auto-bind": "^5.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "cli-cursor": "^4.0.0", + "cli-truncate": "^3.1.0", + "code-excerpt": "^4.0.0", + "indent-string": "^5.0.0", + "is-ci": "^3.0.1", + "is-lower-case": "^2.0.2", + "is-upper-case": "^2.0.2", + "lodash": "^4.17.21", + "patch-console": "^2.0.0", + "react-reconciler": "^0.29.0", + "scheduler": "^0.23.0", + "signal-exit": "^3.0.7", + "slice-ansi": "^6.0.0", + "stack-utils": "^2.0.6", + "string-width": "^5.1.2", + "type-fest": "^0.12.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0", + "ws": "^8.12.0", + "yoga-wasm-web": "~0.3.3" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "react": ">=18.0.0", + "react-devtools-core": "^4.19.1" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-devtools-core": { + "optional": true + } + } + }, + "node_modules/ink/node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ink/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ink/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ink/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ink/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ink/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/ink/node_modules/type-fest": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", + "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ink/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-actual-promise": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-actual-promise/-/is-actual-promise-1.0.2.tgz", + "integrity": "sha512-xsFiO1of0CLsQnPZ1iXHNTyR9YszOeWKYv+q6n8oSFW3ipooFJ1j1lbRMgiMCr+pp2gLruESI4zb5Ak6eK5OnQ==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", + "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-relative-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz", + "integrity": "sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", + "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-url-superb": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz", + "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsonpath-plus": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.4.0.tgz", + "integrity": "sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lightstep-tracer": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/lightstep-tracer/-/lightstep-tracer-0.31.2.tgz", + "integrity": "sha512-DRdyUrASPkr+hxyHQJ9ImPSIxpUCpqQvfgHwxoZ42G6iEJ2g0/2chCw39tuz60JUmLfTlVp1LFzLscII6YPRoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "1.5.0", + "eventemitter3": "1.1.1", + "google-protobuf": "3.6.1", + "hex2dec": "1.0.1", + "opentracing": "^0.14.4", + "source-map-support": "0.3.3", + "thrift": "^0.14.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/lightstep-tracer/node_modules/async": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.0.tgz", + "integrity": "sha512-m9nMwCtLtz29LszVaR0q/FqsJWkrxVoQL95p7JU0us7qUx4WEcySQgwvuneYSGVyvirl81gz7agflS3V1yW14g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lightstep-tracer/node_modules/eventemitter3": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha512-idmH3G0vJjQv2a5N74b+oXcOUKYBqSGJGN1eVV6ELGdUnesAO8RZsU74eaS3VfldRet8N9pFupxppBUKztrBdQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/matcher-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.1.2.tgz", + "integrity": "sha512-YQ/teqaOIIfUHedRam08PB3NK7Mjct6BvzRnJmpGDm8uFXpNr1sbY4yuflI5JcEs6COpYA0FpRQhSDBf1tT95g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.2" + } + }, + "node_modules/matcher-collection/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/matcher-collection/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/matcher-collection/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.2.tgz", + "integrity": "sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mixpanel": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.13.0.tgz", + "integrity": "sha512-YOWmpr/o4+zJ8LPjuLUkWLc2ImFeIkX6hF1t62Wlvq6loC6e8EK8qieYO4gYPTPxxtjAryl7xmIvf/7qnPwjrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "5.0.0" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/mixpanel/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mixpanel/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/module-definition": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-5.0.1.tgz", + "integrity": "sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^5.0.0", + "node-source-walk": "^6.0.1" + }, + "bin": { + "module-definition": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/module-lookup-amd": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-8.0.5.tgz", + "integrity": "sha512-vc3rYLjDo5Frjox8NZpiyLXsNWJ5BWshztc/5KSOMzpg9k5cHH652YsJ7VKKmtM4SvaxuE9RkrYGhiSjH3Ehow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.1", + "glob": "^7.2.3", + "requirejs": "^2.3.6", + "requirejs-config-file": "^4.0.0" + }, + "bin": { + "lookup-amd": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanotimer": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/nanotimer/-/nanotimer-0.3.14.tgz", + "integrity": "sha512-NpKXdP6ZLwZcODvDeyfoDBVoncbrgvC12txO3F4l9BxMycQjZD29AnasGAy7uSi3dcsTGnGn6/zzvQRwbjS4uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", + "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/node-gyp/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-options-to-argv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-options-to-argv/-/node-options-to-argv-1.0.0.tgz", + "integrity": "sha512-99rLlP+Cn/FsSV9kjpk2UmF2Ltmrpv/L9U7fUfws/MVXkeZWPpPDsQkMr79qCvSF/oTKVVJBTm5sHzmK2j6IIg==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/node-source-walk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-6.0.2.tgz", + "integrity": "sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.21.8" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.2.1.tgz", + "integrity": "sha512-8l+7jxhim55S85fjiDGJ1rZXBWGtRLi1OSb4Z3BPLObPuIaeKRlPRiYMSHU4/81ck3t71Z+UwDDl47gcpmfQQA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^1.1.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/opentracing": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.7.tgz", + "integrity": "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ora": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", + "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "17.0.7", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.7.tgz", + "integrity": "sha512-sgvnoUMlkv9xHwDUKjKQFXVyUi8dtJGKp3vg6sYy+TxbDic5RjZCHF3ygv0EJgNRZ2GfRONjlKPUfokJ9lDpwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/patch-console": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", + "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/polite-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-4.0.1.tgz", + "integrity": "sha512-8LI5ZeCPBEb4uBbcYKNVwk4jgqNx1yHReWoW4H4uUihWlSqZsUDfSITrRhjliuPgxsNPFhNSudGO2Zu4cbWinQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-values-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz", + "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "color-name": "^1.1.4", + "is-url-superb": "^4.0.0", + "quote-unquote": "^1.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.2.9" + } + }, + "node_modules/postcss-values-parser/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/posthog-node": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.6.0.tgz", + "integrity": "sha512-/BiFw/jwdP0uJSRAIoYqLoBTjZ612xv74b1L/a3T/p1nJVL8e0OrHuxbJW56c6WVW/IKm9gBF/zhbqfaz0XgJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^0.27.0" + }, + "engines": { + "node": ">=15.0.0" + } + }, + "node_modules/precinct": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-11.0.5.tgz", + "integrity": "sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dependents/detective-less": "^4.1.0", + "commander": "^10.0.1", + "detective-amd": "^5.0.2", + "detective-cjs": "^5.0.1", + "detective-es6": "^4.0.1", + "detective-postcss": "^6.1.3", + "detective-sass": "^5.0.3", + "detective-scss": "^4.0.3", + "detective-stylus": "^4.0.0", + "detective-typescript": "^11.1.0", + "module-definition": "^5.0.1", + "node-source-walk": "^6.0.2" + }, + "bin": { + "precinct": "bin/cli.js" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + } + }, + "node_modules/present": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/present/-/present-0.0.3.tgz", + "integrity": "sha512-d0QMXYTKHuAO0n0IfI/x2lbNwybdNWjRQ08hQySzqMQ2M0gwh/IetTv2glkPJihFn+cMDYjK/BiVgcLcjsASgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prismjs-terminal": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prismjs-terminal/-/prismjs-terminal-1.2.4.tgz", + "integrity": "sha512-S2nsjy6s2x2jF4uTW8ulX19rvmRfe9R1wmnNwI5wmBgQEErB0vuKueVPMzN6KsFRCCJ2IQrWUS0BqhcNsrR9xg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "chalk": "^5.2.0", + "prismjs": "^1.30.0", + "string-length": "^6.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/prismjs-terminal/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prom-client": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", + "integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tdigest": "^0.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz", + "integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quote-unquote": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz", + "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-element-to-jsx-string": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz", + "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@base2/pretty-print-object": "1.0.1", + "is-plain-object": "5.0.0", + "react-is": "18.1.0" + }, + "peerDependencies": { + "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", + "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/react-is": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", + "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-reconciler": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz", + "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/read-package-json": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.1.tgz", + "integrity": "sha512-8PcDiZ8DXUjLf687Ol4BR8Bpm2umR7vhoZOzNRt+uxD9GpBh/K+CAAALVIiYFknmvlmyg7hM7BSNUXPaCCqd0Q==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requirejs": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.8.tgz", + "integrity": "sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==", + "dev": true, + "license": "MIT", + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/requirejs-config-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz", + "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "^4.0.0", + "stringify-object": "^3.2.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-dependency-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-3.0.2.tgz", + "integrity": "sha512-Tz7zfjhLfsvR39ADOSk9us4421J/1ztVBo4rWUkF38hgHK5m0OCZ3NxFVpqHRkjctnwVa15igEUHFJp8MCS7vA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/resolve-import": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/resolve-import/-/resolve-import-1.4.6.tgz", + "integrity": "sha512-CIw9e64QcKcCFUj9+KxUCJPy8hYofv6eVfo3U9wdhCm2E4IjvFnZ6G4/yIC4yP3f11+h6uU5b3LdS7O64LgqrA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^10.3.3", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/resolve-import/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-import/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/resolve-import/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/resolve-import/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass-lookup": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-5.0.1.tgz", + "integrity": "sha512-t0X5PaizPc2H4+rCwszAqHZRtr4bugo4pgiCvrBFvIX0XFxnr29g77LJcpyj9A0DcKf7gXMLcgvRjsonYI6x4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.1" + }, + "bin": { + "sass-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "dev": true, + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-6.0.0.tgz", + "integrity": "sha512-6bn4hRfkTvDfUoEQYkERg0BVF1D0vrX9HEkMl08uDiNWvVvjylLHvZFZWkDo6wjT8tUctbYl1nCOuE66ZTaUtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socketio-wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/socketio-wildcard/-/socketio-wildcard-2.0.0.tgz", + "integrity": "sha512-Bf3ioZq15Z2yhFLDasRvbYitg82rwm+5AuER5kQvEQHhNFf4R4K5o/h57nEpN7A59T9FyRtTj34HZfMWAruw/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.3.3.tgz", + "integrity": "sha512-9O4+y9n64RewmFoKUZ/5Tx9IHIcXM6Q+RTSw6ehnqybUz4a7iwR3Eaw80uLtqqQ5D0C+5H03D4KKGo9PdP33Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "0.1.32" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "integrity": "sha512-htQyLrrRLkQ87Zfrir4/yN+vAUd6DNjVayEjTSHXu29AYQJw57I4/xEL/M6p6E/woPNJwvZt6rVlzc7gFEJccQ==", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sqs-consumer": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/sqs-consumer/-/sqs-consumer-5.8.0.tgz", + "integrity": "sha512-pJReMEtDM9/xzQTffb7dxMD5MKagBfOW65m+ITsbpNk0oZmJ38tTC4LPmj0/7ZcKSOqi2LrpA1b0qGYOwxlHJg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sdk": "^2.1271.0", + "debug": "^4.3.4" + }, + "peerDependencies": { + "aws-sdk": "^2.1271.0" + } + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz", + "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/stylus-lookup": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-5.0.1.tgz", + "integrity": "sha512-tLtJEd5AGvnVy4f9UHQMw4bkJJtaAcmo54N+ovQBjDY3DuWyK9Eltxzr5+KG0q4ew6v2EHyuWWNnHeiw/Eo7rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.1" + }, + "bin": { + "stylus-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sync-content": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-content/-/sync-content-1.0.2.tgz", + "integrity": "sha512-znd3rYiiSxU3WteWyS9a6FXkTA/Wjk8WQsOyzHbineeL837dLn3DA4MRhsIX3qGcxDMH6+uuFV4axztssk7wEQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^10.2.6", + "mkdirp": "^3.0.1", + "path-scurry": "^1.9.2", + "rimraf": "^5.0.1" + }, + "bin": { + "sync-content": "dist/mjs/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sync-content/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sync-content/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sync-content/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sync-content/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/tap/-/tap-19.2.5.tgz", + "integrity": "sha512-Mz7MznUuKCqrN9dr0s8REt6zLg6WLNrvGXwDSaUyPO73dpXXjakYA7YVKRWu6TBnj7NsSYKuHXpQFROlqZ2KTg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@tapjs/after": "1.1.31", + "@tapjs/after-each": "2.0.8", + "@tapjs/asserts": "2.0.8", + "@tapjs/before": "2.0.8", + "@tapjs/before-each": "2.0.8", + "@tapjs/chdir": "1.1.4", + "@tapjs/core": "2.1.6", + "@tapjs/filter": "2.0.8", + "@tapjs/fixture": "2.0.8", + "@tapjs/intercept": "2.0.8", + "@tapjs/mock": "2.1.6", + "@tapjs/node-serialize": "2.0.8", + "@tapjs/run": "2.1.7", + "@tapjs/snapshot": "2.0.8", + "@tapjs/spawn": "2.0.8", + "@tapjs/stdin": "2.0.8", + "@tapjs/test": "2.2.4", + "@tapjs/typescript": "1.4.13", + "@tapjs/worker": "2.0.8", + "resolve-import": "^1.4.5" + }, + "bin": { + "tap": "dist/esm/run.mjs" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap-parser": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-16.0.1.tgz", + "integrity": "sha512-vKianJzSSzLkJ3bHBwzvZDDRi9yGMwkRANJxwPAjAue50owB8rlluYySmTN4tZVH0nsh6stvrQbg9kuCL5svdg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "events-to-array": "^2.0.3", + "tap-yaml": "2.2.2" + }, + "bin": { + "tap-parser": "bin/cmd.cjs" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + } + }, + "node_modules/tap-yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-2.2.2.tgz", + "integrity": "sha512-MWG4OpAKtNoNVjCz/BqlDJiwTM99tiHRhHPS4iGOe1ZS0CgM4jSFH92lthSFvvy4EdDjQZDV7uYqUFlU9JuNhw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "yaml": "^2.4.1", + "yaml-types": "^0.3.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tcompare": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-7.0.1.tgz", + "integrity": "sha512-JN5s7hgmg/Ya5HxZqCnywT+XiOGRFcJRgYhtMyt/1m+h0yWpWwApO7HIM8Bpwyno9hI151ljjp5eAPCHhIGbpQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "diff": "^5.2.0", + "react-element-to-jsx-string": "^15.0.0" + }, + "engines": { + "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" + } + }, + "node_modules/tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bintrees": "1.0.2" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/thrift": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/thrift/-/thrift-0.14.2.tgz", + "integrity": "sha512-bW8EaE6iw3hSt4HB2HpBdHW86Xpb9IUJfqufx4NwEu7OGuIpS0ISj+Yy1Z1Wvhfno6SPNhKRJ1qFXea84HcrOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "browser-or-node": "^1.2.1", + "isomorphic-ws": "^4.0.1", + "node-int64": "^0.4.0", + "q": "^1.5.0", + "ws": "^5.2.2" + }, + "engines": { + "node": ">= 10.18.0" + } + }, + "node_modules/thrift/node_modules/ws": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.4.tgz", + "integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/trivial-deferred": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-2.0.0.tgz", + "integrity": "sha512-iGbM7X2slv9ORDVj2y2FFUq3cP/ypbtu2nQ8S38ufjL0glBABvmR9pTdsib1XtS2LUhhLMbelaBUaf/s5J3dSw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 8" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tshy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/tshy/-/tshy-1.18.0.tgz", + "integrity": "sha512-FQudIujBazHRu7CVPHKQE9/Xq1Wc7lezxD/FCnTXx2PTcnoSN32DVpb/ZXvzV2NJBTDB3XKjqX8Cdm+2UK1DlQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "chalk": "^5.3.0", + "chokidar": "^3.6.0", + "foreground-child": "^3.1.1", + "minimatch": "^9.0.4", + "mkdirp": "^3.0.1", + "polite-json": "^5.0.0", + "resolve-import": "^1.4.5", + "rimraf": "^5.0.1", + "sync-content": "^1.0.2", + "typescript": "5", + "walk-up-path": "^3.0.1" + }, + "bin": { + "tshy": "dist/esm/index.js" + }, + "engines": { + "node": "16 >=16.17 || 18 >=18.15.0 || >=20.6.1" + } + }, + "node_modules/tshy/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tshy/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/tshy/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tshy/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tshy/node_modules/polite-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-5.0.0.tgz", + "integrity": "sha512-OLS/0XeUAcE8a2fdwemNja+udKgXNnY6yKVIXqAD2zVRx1KvY6Ato/rZ2vdzbxqYwPW0u6SCNC/bAMPNzpzxbw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unix-dgram": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.7.tgz", + "integrity": "sha512-pWaQorcdxEUBFIKjCqqIlQaOoNVmchyoaNAJ/1LwyyfK2XSxcBhgJNiSE8ZRhR0xkNGyk4xInt1G03QPoKXY5A==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.20.0" + }, + "engines": { + "node": ">=0.10.48" + } + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/walk-sync": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.2.7.tgz", + "integrity": "sha512-OH8GdRMowEFr0XSHQeX5fGweO6zSVHo7bG/0yJQx6LAj9Oukz0C8heI3/FYectT66gY0IPGe89kOvU410/UNpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ensure-posix-path": "^1.0.0", + "matcher-collection": "^1.0.0" + } + }, + "node_modules/walk-up-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", + "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-js": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/yaml-js/-/yaml-js-0.2.3.tgz", + "integrity": "sha512-6xUQtVKl1qcd0EXtTEzUDVJy9Ji1fYa47LtkDtYKlIjhibPE9knNPmoRyf6SGREFHlOAUyDe9OdYqRP4DuSi5Q==", + "dev": true, + "license": "WTFPL" + }, + "node_modules/yaml-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yaml-types/-/yaml-types-0.3.0.tgz", + "integrity": "sha512-i9RxAO/LZBiE0NJUy9pbN5jFz5EasYDImzRkj8Y81kkInTi1laia3P3K/wlMKzOxFQutZip8TejvQP/DwgbU7A==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 16", + "npm": ">= 7" + }, + "peerDependencies": { + "yaml": "^2.3.0" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoga-wasm-web": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz", + "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + } +} diff --git a/tests/loadtesting/package.json b/tests/loadtesting/package.json index 520911d0b..38b1e4c9c 100644 --- a/tests/loadtesting/package.json +++ b/tests/loadtesting/package.json @@ -6,10 +6,10 @@ "scripts": { "dev": "node functions.js", "get-keycloak-token": "node functions.js --get-keycloak-token", - "tests": "npx artillery run csr-http-load-test-appointments.yaml", - "tests:all": "source envs.sh && npx artillery run --output output/report.json -k csr-test-all.yaml", - "tests:http": "source envs.sh && npx artillery run --output output/report-http.json -k csr-http.yaml", - "tests:socket": "source envs.sh && npx artillery run --output output/report-socket.json -k csr-socket.yaml", + "tests": "mkdir -p output && . ./envs.sh && node run-artillery.js csr-http.yaml --output output/report-http.json -k", + "tests:all": "mkdir -p output && . ./envs.sh && node run-artillery.js csr-test-all.yaml --output output/report.json -k", + "tests:http": "mkdir -p output && . ./envs.sh && node run-artillery.js csr-http.yaml --output output/report-http.json -k", + "tests:socket": "mkdir -p output && . ./envs.sh && node run-artillery.js csr-socket.yaml --output output/report-socket.json -k", "python:profile": "sudo ./profile-python.sh --command=record || exit 0", "python:top": "sudo ./profile-python.sh --command=top || exit 0" }, diff --git a/tests/loadtesting/run-artillery.js b/tests/loadtesting/run-artillery.js new file mode 100644 index 000000000..3bc8ad873 --- /dev/null +++ b/tests/loadtesting/run-artillery.js @@ -0,0 +1,56 @@ +const fs = require('fs'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +function renderProcessEnvironment(template) { + return template.replace(/\{\{\s*\$processEnvironment\.([A-Z0-9_]+)\s*\}\}/g, (match, variableName) => { + const value = process.env[variableName]; + + if (value === undefined) { + throw new Error(`Missing required environment variable: ${variableName}`); + } + + return value; + }); +} + +function main() { + const [scriptFile, ...artilleryArgs] = process.argv.slice(2); + + if (!scriptFile) { + throw new Error('Usage: node run-artillery.js [artillery args]'); + } + + const sourcePath = path.resolve(__dirname, scriptFile); + const source = fs.readFileSync(sourcePath, 'utf8'); + const rendered = renderProcessEnvironment(source); + const renderedPath = path.join( + path.dirname(sourcePath), + `.artillery-${path.basename(scriptFile, path.extname(scriptFile))}-${process.pid}.yaml` + ); + + fs.writeFileSync(renderedPath, rendered); + + let result; + try { + result = spawnSync( + 'npx', + ['artillery', 'run', ...artilleryArgs, renderedPath], + { + cwd: __dirname, + env: process.env, + stdio: 'inherit', + } + ); + } finally { + fs.unlinkSync(renderedPath); + } + + if (result.error) { + throw result.error; + } + + process.exit(result.status === null ? 1 : result.status); +} + +main(); From 9baeebfd44e11cf6de73a44c22331924a1bcf310 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 24 Apr 2026 09:11:56 -0700 Subject: [PATCH 40/81] Fix newman tests --- .../bookings/appointment/appointment_post.py | 35 +- .../bookings/appointment/appointment_put.py | 40 +- .../theq/citizen/citizen_generic_invite.py | 2 +- .../resources/theq/service_requests_list.py | 13 +- api/app/resources/theq/user/user.py | 19 +- api/app/tests/auth/test_public_user_routes.py | 10 + .../test_reference_data_contracts.py | 7 + api/app/tests/flows/test_queue_flows.py | 28 + .../tests/flows/test_service_request_flows.py | 18 + .../validation/test_appointment_validation.py | 41 + api/manage.py | 14 + api/postman/API_Test_TheQ_Booking.json | 34393 +++++++++------- .../API_Test_TheQ_Booking_with_QTxn.json | 28260 +++++++------ api/postman/Load_Test_Booking.json | 6076 +-- api/postman/Load_Test_Exam.json | 7354 ++-- api/postman/Load_Test_TheQ.json | 10017 ++--- api/postman/README-local-auth.md | 55 + api/postman/package.json | 5 + documentation/Readme.md | 37 +- keycloak-local/servicebc-local-realm.json | 98 + 20 files changed, 46190 insertions(+), 40332 deletions(-) create mode 100644 api/postman/README-local-auth.md create mode 100644 api/postman/package.json diff --git a/api/app/resources/bookings/appointment/appointment_post.py b/api/app/resources/bookings/appointment/appointment_post.py index 1ffa82732..e8bd3f38e 100644 --- a/api/app/resources/bookings/appointment/appointment_post.py +++ b/api/app/resources/bookings/appointment/appointment_post.py @@ -35,6 +35,18 @@ from app.utilities.sms import send_sms +def _get_valid_service(service_id): + if service_id in (None, ""): + return None + + try: + service_id = int(service_id) + except (TypeError, ValueError): + return None + + return db.session.get(Service, service_id) + + @api.route("/appointments/", methods=["POST"]) class AppointmentPost(Resource): appointment_schema = AppointmentSchema() @@ -87,7 +99,11 @@ def post(self): citizen.citizen_name = user.display_name office = Office.find_by_id(office_id) - service = db.session.get(Service, int(service_id)) + service = _get_valid_service(service_id) + if service is None: + return { + "message": "Could not find service for service_id: " + str(service_id) + }, 400 # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id(office_id=office_id, @@ -115,6 +131,23 @@ def post(self): csr = CSR.find_by_username(get_username()) office_id = csr.office_id office = Office.find_by_id(office_id) + service_id = json_data.get('service_id') + + # Preserve legacy Newman blackout payloads, which omit service_id for + # internal recurring blackout appointments. + if not (is_blackout_appt and service_id in (None, "")): + service = _get_valid_service(service_id) + if service is None: + return { + "message": "Could not find service for service_id: " + str(service_id) + }, 400 + else: + service = None + + if service is None and not is_blackout_appt: + return { + "message": "Could not find service for service_id: " + str(service_id) + }, 400 citizen.office_id = office_id citizen.qt_xn_citizen_ind = 0 diff --git a/api/app/resources/bookings/appointment/appointment_put.py b/api/app/resources/bookings/appointment/appointment_put.py index 5512cb2ee..38a18b9c6 100644 --- a/api/app/resources/bookings/appointment/appointment_put.py +++ b/api/app/resources/bookings/appointment/appointment_put.py @@ -29,6 +29,19 @@ from qsystem import socketio, application from app.utilities.sms import send_sms + +def _get_valid_service(service_id): + if service_id in (None, ""): + return None + + try: + service_id = int(service_id) + except (TypeError, ValueError): + return None + + return db.session.get(Service, service_id) + + @api.route("/appointments//", methods=["PUT"]) class AppointmentPut(Resource): appointment_schema = AppointmentSchema() @@ -54,6 +67,17 @@ def put(self, id): if is_public_user_appt: office_id = json_data.get('office_id') office = Office.find_by_id(office_id) + appointment = Appointment.query.filter_by(appointment_id=id) \ + .filter_by(office_id=office_id) \ + .first_or_404() + + service_id = json_data.get('service_id', appointment.service_id) + service = _get_valid_service(service_id) + if service is None: + return { + "message": "Could not find service for service_id: " + str(service_id) + }, 400 + # user = PublicUser.find_by_username(g.oidc_token_info['username']) # citizen = Citizen.find_citizen_by_username(g.oidc_token_info['username'], office_id) # Validate if the same user has other appointments for same day at same office @@ -69,8 +93,6 @@ def put(self, id): # Check for race condition start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) - service_id = json_data.get('service_id') - service = db.session.get(Service, int(service_id)) if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time, service=service): return {"code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to scheduling conflict. Please pick another time."}, 400 @@ -79,10 +101,16 @@ def put(self, id): csr = CSR.find_by_username(get_username()) office_id = csr.office_id office = Office.find_by_id(office_id) - - appointment = Appointment.query.filter_by(appointment_id=id) \ - .filter_by(office_id=office_id) \ - .first_or_404() + if 'service_id' in json_data: + service = _get_valid_service(json_data.get('service_id')) + if service is None: + return { + "message": "Could not find service for service_id: " + str(json_data.get('service_id')) + }, 400 + + appointment = Appointment.query.filter_by(appointment_id=id) \ + .filter_by(office_id=office_id) \ + .first_or_404() # If appointment is not made by same user, throw error if is_public_user_appt: diff --git a/api/app/resources/theq/citizen/citizen_generic_invite.py b/api/app/resources/theq/citizen/citizen_generic_invite.py index bdf7e8864..7fc61a346 100644 --- a/api/app/resources/theq/citizen/citizen_generic_invite.py +++ b/api/app/resources/theq/citizen/citizen_generic_invite.py @@ -127,7 +127,7 @@ def post(self): waiting_period_state = find_wait() citizen = None - json_data = request.get_json() + json_data = request.get_json(silent=True) or {} if json_data and 'counter_id' in json_data: counter_id = int(json_data.get('counter_id')) diff --git a/api/app/resources/theq/service_requests_list.py b/api/app/resources/theq/service_requests_list.py index 9836d683a..2e5864eba 100644 --- a/api/app/resources/theq/service_requests_list.py +++ b/api/app/resources/theq/service_requests_list.py @@ -68,12 +68,18 @@ def get_service(service_request, json_data, csr): service = None try: service = db.session.get(Service, service_request.service_id) - except: + except Exception: logging.exception("==> An exception getting service info") logging.exception(csr_const + csr.username) logging.exception(json_data_const + json.dumps(json_data['service_request'])) return (None, ("Could not find service for service_id: " + str(service_request.service_id)), 400) + if service is None: + logging.info("==> No service found in POST /service_requests/") + logging.info(csr_const + csr.username) + logging.info(json_data_const + json.dumps(json_data['service_request'])) + return (None, ("Could not find service for service_id: " + str(service_request.service_id)), 400) + if service.parent_id is None: logging.info("==> CSR has selected a category, rather than a service. This should not be possible") logging.info(csr_const + csr.username) @@ -92,10 +98,13 @@ class ServiceRequestsList(Resource): @api_call_with_retry def post(self): try: - json_data = request.get_json() + json_data = request.get_json(silent=True) except Exception as error: return {"message": str(error)}, 401 + if json_data is None: + return {"message": "No input data received for creating service request"}, 400 + csr = CSR.find_by_username(get_username()) service_request, message, code = get_service_request(self, json_data, csr) if (service_request is None): diff --git a/api/app/resources/theq/user/user.py b/api/app/resources/theq/user/user.py index b566a2715..b89c10531 100644 --- a/api/app/resources/theq/user/user.py +++ b/api/app/resources/theq/user/user.py @@ -30,6 +30,21 @@ # Defining String constants to appease SonarQube api_down_const = 'API is down' + +def _token_last_name(user_info): + return user_info.get('family_name') or user_info.get('lastName') or '' + + +def _normalize_user_defaults(user: PublicUserModel): + if user.last_name is None: + user.last_name = '' + if user.telephone is None: + user.telephone = '' + if user.send_email_reminders is None: + user.send_email_reminders = False + if user.send_sms_reminders is None: + user.send_sms_reminders = False + @api.route("/users/", methods=['POST']) class PublicUsers(Resource): user_schema = UserSchema(many=False) @@ -47,7 +62,9 @@ def post(self): if not user.email: user.email = user_info.get('email') user.display_name = user_info.get('display_name') - user.last_name = user_info.get('family_name') + if not user.last_name: + user.last_name = _token_last_name(user_info) + _normalize_user_defaults(user) db.session.add(user) db.session.commit() diff --git a/api/app/tests/auth/test_public_user_routes.py b/api/app/tests/auth/test_public_user_routes.py index 1081c5dac..e0a4eb2bd 100644 --- a/api/app/tests/auth/test_public_user_routes.py +++ b/api/app/tests/auth/test_public_user_routes.py @@ -118,3 +118,13 @@ def test_public_user_can_reach_public_user_routes( assert_status(response, case.expected_status) assert response.get_json() is not None + + +def test_public_user_create_backfills_legacy_required_fields(public_client_alt): + """Assert that user creation returns non-null legacy fields expected by the Postman collection.""" + user = create_public_user(public_client_alt) + + assert user["last_name"] == "PublicAlt" + assert user["telephone"] == "" + assert user["send_email_reminders"] is False + assert user["send_sms_reminders"] is False diff --git a/api/app/tests/contracts/test_reference_data_contracts.py b/api/app/tests/contracts/test_reference_data_contracts.py index 78581859d..cd4f1eb62 100644 --- a/api/app/tests/contracts/test_reference_data_contracts.py +++ b/api/app/tests/contracts/test_reference_data_contracts.py @@ -64,6 +64,13 @@ def test_services_contract_includes_parent_relationships(internal_ga_client): assert service["parent_id"] is not None assert service["parent"]["service_name"] + rural_ptax = next( + service + for service in body["services"] + if service["service_name"] == "Other - Rural PTAX" + ) + assert rural_ptax["parent"]["service_name"] == "Property Tax" + def test_office_scoped_services_filter_deleted_entries_and_preserve_sort_order( internal_ga_client, seeded_data, app diff --git a/api/app/tests/flows/test_queue_flows.py b/api/app/tests/flows/test_queue_flows.py index ad338a54f..202ff8eeb 100644 --- a/api/app/tests/flows/test_queue_flows.py +++ b/api/app/tests/flows/test_queue_flows.py @@ -82,6 +82,34 @@ def test_qt1_specific_invite_appends_an_invited_period(internal_ga_client, seede ) +def test_generic_invite_accepts_an_empty_post_body( + internal_ga_client, seeded_data +): + """Assert that legacy empty-body invites still default to the CSR counter under Flask 3.""" + citizen, _service_request, queued_citizen = _create_queue_ready_citizen( + internal_ga_client, + seeded_data, + position=0, + name="Empty Body Invite Citizen", + service_id_key="ptax", + channel_id_key="phone", + quantity=1, + counter_id_key="counter", + qt_xn_citizen_ind=0, + ) + + queued_period_count = len(_primary_service_request(queued_citizen)["periods"]) + invited_citizen = _citizen_from_response( + internal_ga_client.post("/citizens/invite/", data="") + ) + + assert invited_citizen["citizen_id"] == citizen["citizen_id"] + assert _latest_period_name(_primary_service_request(invited_citizen)) == "Invited" + _assert_period_count_delta( + _primary_service_request(invited_citizen), queued_period_count + ) + + def test_qt1_begin_service_after_invite_appends_a_being_served_period( internal_ga_client, seeded_data ): diff --git a/api/app/tests/flows/test_service_request_flows.py b/api/app/tests/flows/test_service_request_flows.py index ba27b3ea0..d0d42bc8a 100644 --- a/api/app/tests/flows/test_service_request_flows.py +++ b/api/app/tests/flows/test_service_request_flows.py @@ -61,6 +61,24 @@ def test_service_request_create_rejects_category_selection( assert "category" in json_of(response)["message"].lower() +def test_service_request_create_rejects_unknown_service_id( + internal_ga_client, seeded_data +): + """Assert that unknown service ids return a stable JSON 400 instead of an HTML 500.""" + citizen = create_citizen(internal_ga_client, 0, name="Invalid Service Citizen") + response = internal_ga_client.post( + "/service_requests/", + json=_service_request_payload( + citizen["citizen_id"], + seeded_data, + service_id=999999, + ), + ) + + assert_json_response(response, 400) + assert json_of(response)["message"] == "Could not find service for service_id: 999999" + + def test_first_service_request_assigns_ticket_numbers_and_choose_service_event( internal_ga_client, seeded_data, app, monkeypatch ): diff --git a/api/app/tests/validation/test_appointment_validation.py b/api/app/tests/validation/test_appointment_validation.py index 182b9a777..39418998e 100644 --- a/api/app/tests/validation/test_appointment_validation.py +++ b/api/app/tests/validation/test_appointment_validation.py @@ -4,9 +4,11 @@ from app.tests.api_test_support import ( assert_json_response, create_public_user, + future_utc_window, json_of, public_slot_payload, slot_window_to_iso, + unique_name, ) pytestmark = [pytest.mark.validation, pytest.mark.usefixtures("seeded_database")] @@ -148,3 +150,42 @@ def test_public_user_create_rejects_when_the_daily_limit_is_reached( "code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appointments reached", } + + +def test_internal_appointment_create_rejects_unknown_service_id( + internal_ga_client, seeded_data +): + """Assert that internal appointment creation returns JSON 400 for invalid service ids.""" + start_time, end_time = future_utc_window(2) + response = internal_ga_client.post( + "/appointments/", + json={ + "service_id": 999999, + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal invalid service", + "citizen_name": unique_name("invalid-service"), + "contact_information": "internal@example.com", + }, + ) + + assert_json_response(response, 400) + assert json_of(response)["message"] == "Could not find service for service_id: 999999" + + +def test_public_appointment_create_rejects_unknown_service_id( + public_client, seeded_data +): + """Assert that public appointment creation returns JSON 400 for invalid service ids.""" + create_public_user(public_client) + payload, _day_key, _slots = public_slot_payload( + public_client, seeded_data, minimum_slots=1 + ) + response = public_client.post( + "/appointments/", + json={**payload, "service_id": 999999}, + ) + + assert_json_response(response, 400) + assert json_of(response)["message"] == "Could not find service for service_id: 999999" diff --git a/api/manage.py b/api/manage.py index 30a22329c..99f8fba55 100644 --- a/api/manage.py +++ b/api/manage.py @@ -357,6 +357,17 @@ def bootstrap(): display_dashboard_ind=1, actual_service_ind=1 ) + service_ptax5 = theq.Service( + service_code='PTAX - 005', + service_name='Other - Rural PTAX', + service_desc='PTax/RPT - Providing information, forms, searches, ' + 'tax clearance certificate, address changes, add new owner, ' + 'extensions, forfeiture status, tax search, etc.', + parent_id=category_ptax.service_id, + prefix='A', + display_dashboard_ind=1, + actual_service_ind=1 + ) service_ptax1 = theq.Service( service_code='PTAX - 001', service_name='Deferment Application', @@ -453,6 +464,7 @@ def bootstrap(): db.session.add(service_ptax1) db.session.add(service_ptax2) db.session.add(service_ptax4) + db.session.add(service_ptax5) db.session.add(service_exams) db.session.add(service_dlkt) db.session.commit() @@ -737,6 +749,7 @@ def bootstrap(): office_test.services.append(service_ptax1) office_test.services.append(service_ptax2) office_test.services.append(service_ptax4) + office_test.services.append(service_ptax5) office_test.services.append(service_exams) office_test.services.append(service_dlkt) @@ -758,6 +771,7 @@ def bootstrap(): office_100.services.append(service_ptax1) office_100.services.append(service_ptax2) office_100.services.append(service_ptax4) + office_100.services.append(service_ptax5) office_100.services.append(service_dlkt) db.session.commit() diff --git a/api/postman/API_Test_TheQ_Booking.json b/api/postman/API_Test_TheQ_Booking.json index 9f00e0249..68d2ef7f8 100644 --- a/api/postman/API_Test_TheQ_Booking.json +++ b/api/postman/API_Test_TheQ_Booking.json @@ -1,15938 +1,18457 @@ { - "info": { - "_postman_id": "614e41a1-da60-4f20-be17-c5fd6b52c6d2", - "name": "API_Test_TheQ_Booking Copy6-9", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Setup TheQ", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "", - " pm.globals.set('public_url', pm.globals.get(usePrefix + 'public_url'));", - " pm.globals.set('public_user_id', pm.globals.get(usePrefix + 'public_user_id'));", - " pm.globals.set('public_user_password', pm.globals.get(usePrefix + 'public_user_password'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "console.log(\" --> public_url: \" + pm.globals.get(\"public_url\"));", - "console.log(\" --> public_user_id: \" + pm.globals.get(\"public_user_id\"));", - "console.log(\" --> public_user_id: \" + pm.globals.get(\"public_user_password\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.globals.set(\"token\", jsonData.access_token);\n\t pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n\t if (err) {\n\t console.log(err);\n\t }\n\t // console.log(err ? err : res.json());\n\t} else {\n\t pm.globals.set(\"token\", 0);\n\t pm.globals.set(\"refresh_token\", 0);\n\t pm.globals.set(\"token_expires\", 0);\n\t pm.globals.set(\"refresh_token_expires\", 0);\n\t}\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-Script", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-AuthToken-Script", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_token_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: authurl + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\n\npm.sendRequest(echoPostRequest, function (err, res) {\n if (err) { console.log(err); }\n else {\n var jsonData = res.json();\n pm.globals.set(\"token\", jsonData.access_token);\n pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n pm.globals.set(\"token_expires\", Date.now()+(jsonData.expires_in * 1000));\n pm.globals.set(\"refresh_token_expires\", Date.now()+(jsonData.refresh_expires_in * 1000));\n }\n //console.log(err ? err : res.json());\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-AuthRefresh-Script", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_refresh_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nrefresh_token = environment.refresh_token;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: authurl +'/auth/realms/' +realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=refresh_token&client_id=' + clientid \n + '&refresh_token=' + refresh_token \n + '&client_secret=' + client_secret\n }\n};\n\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n pm.globals.set(\"token\", jsonData.access_token);\n pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n pm.globals.set(\"token_expires\", Date.now()+(jsonData.expires_in * 1000));\n pm.globals.set(\"refresh_token_expires\", Date.now()+(jsonData.refresh_expires_in * 1000));\n\n console.log(err ? err : res.json());\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\r\nmax_response_time = JSON.parse(globals.max_response_time);\r\n\r\n// Check to make sure the response time was within the maximum allowed.\r\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\r\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\r\n});\r\n\r\n// Other tests.\r\npm.test(\"Response code for request is 200\", function(){\r\n pm.response.to.have.status(200);\r\n});\r\npm.test('Response header should have Content-Type of application/json', function() {\r\n pm.response.to.have.header('content-type', 'application/json');\r\n});\r\npm.test('Response body be in JSON format', function() {\r\n pm.response.to.be.json; \r\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Complex-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"complex_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Create-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.response.to.have.status(201);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Citizen-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"citizen_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var citizenSchema = {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": [\"null\", \"string\"]},\n \"citizen_name\": {\"type\": [\"null\", \"string\"]},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": [\"null\", \"number\"]},\n \"service_reqs\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\": {\"type\": \"number\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"periods\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"},\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]\n }\n },\n \"quantity\": {\"type\": \"number\"},\n \"service\": {\n \"type\": \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"},\n },\n \"required\": [\"external_service_name\", \"online_availability\", \"online_link\", \"parent\", \"parent_id\", \"service_name\"]\n },\n \"service_id\": {\"type\": \"number\"},\n \"sr_id\": {\"type\": \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n }\n },\n \"required\": [\"channel\", \"channel_id\", \"citizen_id\", \"periods\", \"quantity\", \"service\", \"service_id\",\n \"sr_id\", \"sr_number\", \"sr_state\"]\n }\n },\n \"user\": {\"type\": [\"null\", \"object\"]},\n \"citizen_id\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": [\"null\", \"number\"]},\n \"counter\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_comments\": {\"type\": [\"null\", \"string\"]},\n \"priority\": {\"type\": \"number\"},\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n },\n \"required\": [\"accurate_time_ind\", \"ticket_number\", \"citizen_name\", \"qt_xn_citizen_ind\", \"user_id\",\n \"service_reqs\", \"citizen_id\", \"counter_id\", \"start_time\",\n \"citizen_comments\", \"priority\", \"cs\", \"office_id\"]\n },\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Citizen Schema\", function(){\n pm.expect(tv4.validate(allElements, citizenSchema)).to.be.true;\n});\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n // tests[testTitle + \"conforms to schema\"] = tv4.validate(element, citizenSchema);\n\n //Test to see if response schema is valid\n pm.test(testTitle + \"conforms to schema\", function(){\n pm.expect(tv4.validate(allElements, citizenSchema)).to.be.true;\n });\n\n\n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Service-Schema-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"service_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the schema.\nvar serviceSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"data\": {\n \n \"properties\": {\n \"services\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"services\", \"errors\"],\n}\n}\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Service Schema\", function(){\n pm.expect(tv4.validate(jsonData, serviceSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Service-Request-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"service_request_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var schema = {\n \"properties\" : {\n \"channel\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"challen_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\" : {\"type\" : \"number\"},\n \"citizen\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": \"string\"},\n \"citizen_name\": {\"type\": \"string\"},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": \"null\"},\n \"user\": {\"type\": \"null\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"counter\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_comments\": {\"type\": \"string\"},\n \"priority\": {\"type\": \"number\"},\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n },\n \"required\": [\"accurate_time_ind\", \"ticket_number\", \"citizen_name\", \"qt_xn_citizen_ind\",\n \"user_id\", \"citizen_id\", \"counter_id\", \"start_time\",\n \"citizen_comments\", \"priority\", \"cs\", \"office_id\"]\n },\n \"citizen_id\" : {\"type\" : \"number\"},\n \"periods\" : {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"},\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\",\n \"time_end\", \"time_start\"]\n }\n },\n \"quantity\" : {\"type\" : \"number\"},\n \"service\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"},\n },\n \"required\": [\"external_service_name\", \"online_availability\", \"online_link\",\n \"parent\", \"parent_id\", \"service_name\"]\n },\n \"service_id\" : {\"type\" : \"number\"},\n \"sr_id\" : {\"type\" : \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n }\n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-One-Service-Request-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"one_service_request_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var serviceRequestSchema = {\n \"type\": \"object\",\n \"properties\" : {\n \"service_request\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"channel_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\" : {\"type\" : \"number\"},\n \"citizen\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"priority\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"citizen_name\": {\"type\": [\"null\", \"string\"]},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": [\"null\", \"number\"]},\n \"counter_id\": {\"type\": \"number\"},\n \"counter\": {\"type\": \"number\"},\n \"citizen_comments\": {\"type\": \"string\"},\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": [\"null\", \"string\"]},\n \"user\": {\"type\": [\"null\", \"object\"]}\n },\n \"required\": [\"cs\", \"office_id\", \"priority\", \"start_time\", \"citizen_id\", \"citizen_name\", \"qt_xn_citizen_ind\",\n \"user_id\", \"counter_id\", \"citizen_comments\", \"accurate_time_ind\", \"ticket_number\"]\n },\n \"citizen_id\" : {\"type\" : \"number\"},\n \"periods\" : {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]\n }\n },\n \"quantity\" : {\"type\" : \"number\"},\n \"service\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n }\n }\n },\n \"service_id\" : {\"type\" : \"number\"},\n \"sr_id\" : {\"type\" : \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n },\n },\n \"required\" : [\"channel\", \"channel_id\", \"citizen\", \"citizen_id\", \"periods\",\n \"quantity\", \"service\", \"service_id\", \"sr_id\", \"sr_number\", \"sr_state\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"service_request\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Service Request Schema\", function(){\n pm.expect(tv4.validate(jsonData, serviceRequestSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Get-Active-Citizens-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-CSR-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"csr_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar csrSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n },\n \"attention_needed\": {\"type\": \"boolean\"},\n \"active_citizens\": {\"type\": \"array\"},\n \"back_office_display\": {\"type\": \"string\"},\n \"recurring_feature_flag\": {\"type\": \"string\"},\n \"errors\": {}\n },\n \"required\": [\"csr\", \"attention_needed\", \"active_citizens\",\n \"back_office_display\", \"recurring_feature_flag\", \"errors\"],\n};\n\n//Test to see if response schema is valid" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-One-CSR-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"onecsr_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar csrSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n },\n \"errors\" : {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"csr\", \"errors\"]\n};\n\n//Test to see if response schema is valid" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Many-CSRs-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"manycsrs_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar manyCsrsSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csrs\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n }\n }\n }\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate many CSRs Schema\", function(){\n pm.expect(tv4.validate(jsonData, manyCsrsSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Office-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"office_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar officeSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate the Office Schema\", function(){\n pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-All-Offices-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"all_office_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar allOfficeSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"offices\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"max_person_appointment_per_day\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"appointment_days_limit\": {\"type\": \"number\"},\n \"online_status:\": {\"type\": \"string\"},\n \"sb_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n \"office_number\": {\"type\": \"number\"},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n }\n },\n \"back_office_list\": {\"type\": \"array\"},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"appointment_duration\": {\"type\": \"number\"},\n \"quick_list\": {\"type\": \"array\"},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": \"array\"},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\", \"office\", \"start_time\"]\n }\n }\n },\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"offices\", \"errors\"]\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Video-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"video_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar videoSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"videofiles\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\"type\": \"string\"},\n \"date\": {\"type\": \"string\"},\n \"size\": {\"type\": \"string\"}\n },\n \"required\": [\"name\", \"date\", \"size\"]\n }\n },\n \"manifest\": {\"type\": \"string\"},\n \"errors\": {\"type\": \"string\"},\n \"code\": {\"type\": \"number\"},\n \"space\": {\n \"type\": \"object\",\n \"properties\": {\n \"total\": {\"type\": \"number\"},\n \"used\": {\"type\": \"number\"},\n \"freespace\": {\"type\": \"number\"}\n },\n \"required\": [\"total\", \"used\", \"freespace\"]\n }\n },\n \"required\": [\"videofiles\", \"manifest\", \"errors\", \"code\", \"space\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate the Videofiles Schema\", function(){\n pm.expect(tv4.validate(jsonData, videoSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - } - ], - "description": "This folder performs basic authentication features." - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver TheQ", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Set health response time variable.", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Get the maximum load time allowed.", - "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints" - }, - { - "name": "Check user login", - "item": [ - { - "name": "Authenticate default QTxn user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Do the basic checks.", - "eval(environment.basic_response_test);", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " pm.expect(access_token).not.eql(null);", - "});", - "", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " pm.expect(refresh_token).not.eql(null);", - "});", - "", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " pm.expect(expires_in).not.eql(null);", - "});", - "", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " pm.expect(refresh_expires_in).not.eql(null);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "description": "Make sure the operator ID can log in" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.csr_schema_check)", - "", - "// Make sure the postman operator is a GA.", - "if (jsonData.hasOwnProperty(\"csr\")) {", - " role = jsonData.csr.role.role_code;", - "}", - "else {", - " role = \"Unknown\"", - "};", - "", - "pm.test(\"The cfms-postman-operator role is \" + role + \", must be GA\", function() {", - " pm.expect(role).to.be.eql(\"GA\")", - "});", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check channels", - "item": [ - { - "name": "Get channels", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"channel_name\" : {", - " \"type\" : \"string\"", - " },", - " \"channel_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " }", - " },", - " \"required\" : [\"channel_name\", \"channel_id\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allChannels = jsonData.channels;", - "var channelCount = 0;", - "var phoneId = 0;", - "var emailId = 0;", - "var phoneText = \"Phone\";", - "var emailText = \"Email/Fax/Mail\";", - "allChannels.forEach(function(channel) {", - " channelCount ++;", - " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", - " tests[testTitle] = tv4.validate(channel, schema);", - " if (channel.channel_name === phoneText) {", - " phoneId = channel.channel_id;", - " }", - " if (channel.channel_name === emailText) {", - " emailId = channel.channel_id;", - " }", - "});", - "", - "// Check that you found the phone ID.", - "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", - " pm.expect(phoneId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the email ID.", - "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", - " pm.expect(emailId).to.not.be.eql(0);", - "});", - "", - "// Store this ID for future use.", - "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", - "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}channels/", - "host": [ - "{{url}}channels" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check counters", - "item": [ - { - "name": "Store CSR and Office Info", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.csr_schema_check)", - "", - "// Make sure that jsonData has an csr property.", - "pm.test(\"Response should have csr property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", - "});", - "", - "var csr = 0;", - "var office = 0;", - "var counters = 0;", - "var counter_text = \"Counter\";", - "var counter_id = 0;", - "var qtxn_text = \"Quick Trans\";", - "var qtxn_id = 0;", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", - " ", - " csr = jsonData.csr;", - " counter_id = 0;", - " qtxn_id = 0;", - "", - " // Make sure that jsonData has an booking property.", - " pm.test(\"CSR should have office property\", function(){", - " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", - " });", - " ", - " // Make sure office has counter property.", - " if (csr.hasOwnProperty(\"office\")) {", - " office = csr.office;", - " ", - " // Make sure that jsonData has an booking property.", - " pm.test(\"Office should have counters property\", function(){", - " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", - " });", - "", - " // Make sure office has counter property.", - " if (office.hasOwnProperty(\"counters\")) {", - " counters = office.counters;", - " ", - " // Search for Counter and Quick Trans counters", - " counters.forEach(function(counter) {", - " if (counter.counter_name === counter_text) {", - " counter_id = counter.counter_id;", - " }", - " if (counter.counter_name === qtxn_text) {", - " qtxn_id = counter.counter_id;", - " }", - " });", - " ", - " // Make sure you found the right IDs.", - " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", - " pm.expect(counter_id).to.not.be.eql(0);", - " });", - " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", - " pm.expect(qtxn_id).to.not.be.eql(0);", - " });", - " ", - " // Store the ids for future use.", - " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", - " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", - " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", - " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", - " }", - "", - " ", - " }", - "", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check categories", - "item": [ - { - "name": "Get categories", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"actual_service_ind\" : { \"type\" : \"number\" },", - " \"deleted\" : { \"type\" : [\"string\", \"null\"] },", - " \"display_dashboard_ind\" : { \"type\" : \"number\" },", - " \"external_service_name\": {\"type\": [\"null\", \"string\"]},", - " \"online_availability\" : {\"type\": [\"null\", \"string\"]},", - " \"online_link\": {\"type\": [\"null\", \"string\"]},", - " \"parent\": {\"type\": \"null\"},", - " \"parent_id\": {\"type\": \"null\"},", - " \"prefix\" : {\"type\" : \"string\"},", - " \"service_code\" : {\"type\" : \"string\"},", - " \"service_desc\" : {\"type\" : \"string\"},", - " \"service_id\" : {\"type\" : [\"number\", \"object\"]},", - " \"service_name\" : {\"type\" : \"string\"},", - " \"timeslot_duration\": {\"type\": [\"null\", \"number\"]}", - " },", - " \"required\" : [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"external_service_name\",", - " \"online_availability\", \"online_link\", \"parent\", \"parent_id\", \"prefix\", \"service_code\",", - " \"service_desc\", \"service_id\", \"service_name\",", - " \"timeslot_duration\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allCategories = jsonData.categories;", - "var categoryCount = 0;", - "allCategories.forEach(function(category) {", - " categoryCount ++;", - " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", - " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", - " var displayInd = category.display_dashboard_ind;", - " var serviceInd = category.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.eql(0);", - " });", - "", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(0);", - " });", - " ", - " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", - " pm.expect(category.parent_id).to.be.null;", - " });", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}categories/", - "host": [ - "{{url}}categories" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check services", - "item": [ - { - "name": "Get services", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.service_schema_check);", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", - " displayInd = element.display_dashboard_ind;", - " serviceInd = element.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.within(0, 1);", - " });", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(1);", - " });", - " ", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - Rural PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the MSP service.", - "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the Property Tax service.", - "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update quick lists", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.office_schema_check)", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}services/refresh/?office_id={{current_office_id}}", - "host": [ - "{{url}}services" - ], - "path": [ - "refresh", - "" - ], - "query": [ - { - "key": "office_id", - "value": "{{current_office_id}}" - } - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check smartboard", - "item": [ - { - "name": "Get smartboard", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Define the JSON Schema expected in response", - "var smartboardSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_type\": {\"type\": \"string\"},", - " \"citizens\": {", - " \"type\": \"array\",", - " \"items\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"ticket_number\": {\"type\": \"string\"},", - " \"active_period\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"period_id\": {\"type\": \"number\"},", - " \"ps\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"ps_name\": \"string\"", - " },", - " \"required\": [\"ps_name\"]", - " },", - " \"ps_id\": {\"type\": \"number\"},", - " \"time_end\": {\"type\": [\"null\", \"string\"]},", - " \"time_start\": {\"type\": \"string\"}", - " },", - " \"required\": [\"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]", - " },", - " },", - " \"required\": [\"ticket_number\", \"active_period\"]", - " },", - " },", - " },", - " \"required\": [\"office_type\", \"citizens\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate the Smartboard Schema\", function(){", - " pm.expect(tv4.validate(jsonData, smartboardSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}smartboard/?office_number={{current_office_number}}", - "host": [ - "{{url}}smartboard" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check videofiles", - "item": [ - { - "name": "Get videofiles", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.video_schema_check)", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}videofiles/", - "host": [ - "{{url}}videofiles" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Get videofiles for office", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.video_schema_check)", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}videofiles/?office_number={{current_office_number}}", - "host": [ - "{{url}}videofiles" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check CSRs and States", - "item": [ - { - "name": "Get CSRs", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, make sure schema is OK.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.manycsrs_schema_check)", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}csrs/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check Postman Finance No", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.onecsr_schema_check);", - "", - "// Make sure the CSR is not a finance designate.", - "var finance = jsonData.csr.finance_designate;", - "pm.test(\"Finance designate is \" + finance.toString() + \" - it should be 0\", function() {", - " pm.expect(finance).to.be.eql(0);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"finance_designate\": 0\r\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check Postman Finance Yes", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.onecsr_schema_check);", - "", - "// Make sure the CSR is a finance designate.", - "var finance = jsonData.csr.finance_designate;", - "pm.test(\"Finance designate is \" + finance.toString() + \" - it should now be 1\", function() {", - " pm.expect(finance).to.be.eql(1);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"finance_designate\": 1\r\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Get CSR states", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Define the JSON Schema expected in response", - "var csrStateSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_states\" : {", - " \"type\": \"array\",", - " \"items\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"},", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " }", - " },", - " \"errors\": {\"type\": [\"object\", \"string\"]}", - " },", - " \"required\": [\"csr_states\", \"errors\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate CSR States Schema\", function(){", - " pm.expect(tv4.validate(jsonData, csrStateSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}csr_states/", - "host": [ - "{{url}}csr_states" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Clear queue for tests", - "item": [ - { - "name": "Delete citizen queue driver", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Delete citizens, if there are any.", - " if (citizenIds.length > 0) {", - " ", - " // Set the current_client, to be deleted.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(citizenIds));", - " ", - " if (currentCitizen.service_reqs.length === 0) {", - " postman.setNextRequest(\"Next citizen left\");", - " // // Temporary kludge. Citizen left not working, so add SR, then delete.", - " // postman.setNextRequest(\"Temporary add MSP service request\");", - " }", - " else {", - " postman.setNextRequest(\"Next citizen finish service\");", - " }", - " }", - " ", - " // No more citizens. Clear the current, queue variables.", - " else {", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(\"\"));", - " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(\"\"));", - " postman.setNextRequest(\"End clear queue via healthz endpoint\");", - " }", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Next citizen finish service", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "var citizenToBeDeleted = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "var citizenData = jsonData.citizen;", - "var testTitle = \"Check citizen finish service\";", - "", - "// Make sure the response is valid.", - "pm.test(testTitle + \": Response should have property 'citizen'\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"citizen\")).to.be.true;", - "});", - "pm.test(testTitle + \": Response should not have property 'message' indicating an error\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"message\")).to.be.false;", - "});", - "pm.test(testTitle + \": Citizen marked as finished should be citizen \" + citizenToBeDeleted.toString(), function(){", - " pm.expect(citizenData.citizen_id).to.be.eql(citizenToBeDeleted);", - "});", - "", - "// Go back to the delete citizen queue driver.", - "postman.setNextRequest(\"Delete citizen queue driver\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Next citizen citizen left", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform checks.", - " pm.test(\"Check there are no citizens waiting\", function(){", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test(\"Citizen that left must be \" + currentCitizen.citizen_id.toString() + \" (is \" + currentCitizenId.toString(), function(){", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - "}", - "", - "// Go back to the delete citizen queue driver.", - "postman.setNextRequest(\"Delete citizen queue driver\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Temporary add MSP service request", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run create tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "var svcReq = jsonData.service_request;", - "", - "// Run service request tests.", - "eval(environment.one_service_request_test);", - "", - "// Make sure the response is valid.", - "pm.test(\"Response has service_request property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"service_request\")).to.be.true;", - "});", - "pm.test(\"Response has errors property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"errors\")).to.be.true;", - "});", - "", - "// Go back to the clear citizen driver.", - "postman.setNextRequest(\"Next citizen finish service\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : 1,\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "End clear queue via healthz endpoint", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check citizen through queue (QT1)", - "item": [ - { - "name": "Check no citizens (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "", - "// Get the SR ID.", - "if (jsonData.hasOwnProperty(\"service_request\")) {", - "\tcurrentSrId = jsonData.service_request.sr_id;", - "}", - "else {", - " currentSrId = 0;", - "}", - "", - "// The service request ID must not be 0.", - "pm.test(\"The Service Request ID is \" + currentSrId.toString() + \", must not be 0\", function() {", - " pm.expect(currentSrId).to.not.be.eql(0)", - "});", - "", - "// Save the service request ID.", - "postman.setEnvironmentVariable(\"first_sr_id\", currentSrId);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add MSP via email service request (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Activate property tax service (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get the current service request ID.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "", - "if (jsonData.hasOwnProperty(\"service_request\")) {", - "\tcurrentSrId = jsonData.service_request.sr_id;", - "}", - "else {", - " currentSrId = 0;", - "}", - "", - "// Get the first service request ID.", - "first_id = JSON.parse(postman.getEnvironmentVariable(\"first_sr_id\"));", - "", - "// The service request ID must not be 0.", - "pm.test(\"The Service Request ID is \" + currentSrId.toString() + \", must be \" + first_id.toString(), function() {", - " pm.expect(currentSrId).to.be.eql(first_id)", - "});", - "", - "// Save the service request ID.", - "postman.setEnvironmentVariable(\"second_sr_id\", currentSrId);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}service_requests/{{first_sr_id}}/activate/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{first_sr_id}}", - "activate", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be two service requests', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(2);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 5 (five periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(5);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen begin-hold-finish (QT2)", - "item": [ - { - "name": "Check no citizens (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "} ", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Place citizen on hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "place_on_hold", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Get service requests (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_request_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get periods for the first service request.", - " var allPeriods = allElements[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " var allPeriodCount = 0;", - "", - " // Find how many periods there are with null end time.", - " // Also, check schema.", - " allPeriods.forEach(function(onePeriod) {", - " ", - " // Find the open period.", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " ", - " });", - "} ", - "", - "// If there are some service requests, proceed with tests.", - "if (allElements !== null) {", - "", - " // Perform tests.", - " pm.test('There must be only one service request', function() {", - " pm.expect(allElements.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have three periods', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('Service request period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/service_requests/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "service_requests", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Call citizen from hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = allElements[0].service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen leave after create (QT3)", - "item": [ - { - "name": "Check no citizens (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = null;", - " if (currentCitizen.service_reqs.length !== 0) {", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " }", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " if (allPeriods !== null) {", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - " }", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen leave after waiting (QT4)", - "item": [ - { - "name": "Check no citizens (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check no citizens (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check update service information (QT5)", - "item": [ - { - "name": "Check no citizens (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// };", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "", - " // Save the service request ID for later.", - " var mySRId = allElements[0].service_reqs[0].sr_id;", - " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update quantity from 3 to 5 (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_request_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - "", - " // Perform tests.", - " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update service from PropTax to MSP (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_request_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - "", - " // Perform tests.", - " pm.test('Updated service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (should be two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check pick qtxn customer (QT6)", - "item": [ - { - "name": "Check no citizens (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(citizenCount).to.be.eql(0);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Create (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Edit (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Prop Tax via phone (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - List (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Add to queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Create (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/1/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "1", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Edit QTxn (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name_quick\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment_quick\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments,", - "// \"qt_xn_citizen_ind\" : 1", - "// };", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - "", - " // Perform tests.", - " pm.test(\"Should be updating single citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - MSP via email (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - List (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - " // Perform tests.", - " pm.test('Should be updating one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Add to queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should be adding one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check 2 citizens in queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test('Must be two active citizens in the office', function() {", - " pm.expect(citizenCount).to.be.eql(2);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Set CSR to QTxn (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// Turn the qtxn ID from character to number.", - "qtxn_id_number = JSON.parse(environment.qtxn_id);", - "postman.setEnvironmentVariable(\"qtxn_id_number\", qtxn_id_number);" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body, test the schema", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.onecsr_schema_check)", - "", - "// Ensure the CSR is now set to take quick transactions.", - "csrType = jsonData.csr.counter;", - "csrNeed = JSON.parse(environment.qtxn_id_number);", - "pm.test('CSR counter type is ' + csrType.toString() + '; must be ' + csrNeed.toString(), function() {", - " pm.expect(csrType).to.be.eql(csrNeed);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"counter_id\": {{qtxn_id_number}}\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be second one (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - begin serving (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - finish serving (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be first one (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - citizen left (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizen being served\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check pick non-qtxn customer (QT7)", - "item": [ - { - "name": "Check no citizens (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(citizenCount).to.be.eql(0);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Create (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/0/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "0", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Edit QTxn (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - "", - " // Perform tests.", - " pm.test(\"Must be editing only one citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - MSP via email (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - List (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - " // Perform tests.", - " pm.test('Must be editing only one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Add to queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be adding only one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Create (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/1/add_citizen/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "1", - "add_citizen", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Edit (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Should be updating single citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Prop Tax via phone (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get the current service request, check schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.one_service_request_test);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - List (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - " // Perform tests.", - " pm.test('Should be updating one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Add to queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should be adding one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check 2 citizens in queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test('Must be two active citizens in the office', function() {", - " pm.expect(citizenCount).to.be.eql(2);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Set CSR to Counter (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// Turn the qtxn ID from character to number.", - "counter_id_number = JSON.parse(environment.counter_id);", - "postman.setEnvironmentVariable(\"counter_id_number\", counter_id_number);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body, test the schema", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.onecsr_schema_check)", - "", - "// Ensure the CSR is now set to take quick transactions.", - "csrType = jsonData.csr.counter;", - "csrNeed = JSON.parse(environment.counter_id_number);", - "pm.test('CSR counter type is ' + csrType.toString() + '; must be ' + csrNeed.toString(), function() {", - " pm.expect(csrType).to.be.eql(csrNeed);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"counter_id\": {{counter_id_number}}\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be second one (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - begin serving (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - finish serving (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be first one (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - citizen left (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizen being served\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Setup Booking", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "", - "// Hardcode recurring booking and appointment UUIDs.", - "pm.globals.set('pm_booking_uuid', '\"pm_recurring_booking_uuid\"');", - "pm.globals.set('pm_appt_uuid', '\"pm_recurring_appt_uuid\"');", - "", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "", - " pm.globals.set('public_url', pm.globals.get(usePrefix + 'public_url'));", - " pm.globals.set('public_user_id', pm.globals.get(usePrefix + 'public_user_id'));", - " pm.globals.set('public_user_password', pm.globals.get(usePrefix + 'public_user_password'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "console.log(\" --> booking_uuid: \" + pm.globals.get(\"pm_booking_uuid\"));", - "console.log(\" --> appointment_uuid: \" + pm.globals.get(\"pm_appt_uuid\"));", - "console.log(\" --> public_url: \" + pm.globals.get(\"public_url\"));", - "console.log(\" --> public_user_id: \" + pm.globals.get(\"public_user_id\"));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Authentication Token", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " if (pm.expect(access_token).not.eql(null)){", - " pm.globals.set(\"token\", access_token);", - " }", - "});", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " if (pm.expect(refresh_token).not.eql(null)){", - " pm.globals.set(\"refresh_token\", refresh_token);", - " }", - "});", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " if (pm.expect(expires_in).not.eql(null)){", - " pm.globals.set(\"expires_in\", expires_in);", - " }", - "});", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " if (pm.expect(refresh_expires_in).not.eql(null)){", - " pm.globals.set(\"refresh_expires_in\", refresh_expires_in);", - " }", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/x-www-form-urlencoded", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - } - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.globals.set(\"token\", jsonData.access_token);\n\t pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n\t console.log(err ? err : res.json());\n\t} else {\n\t pm.globals.set(\"token\", 0);\n\t pm.globals.set(\"refresh_token\", 0);\n\t pm.globals.set(\"token_expires\", 0);\n\t pm.globals.set(\"refresh_token_expires\", 0);\n\t}\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// If no maximum response defined, set a default.\nresponse_max = 0;\nif (globals.response_max) {\n response_max = JSON.parse(globals.response_max);\n}\nelse {\n response_max = 5009;\n pm.globals.set(\"response_max\", JSON.stringify(response_max));\n};\n\n// Get the max response time allowed.\npm.test('Response time less than ' + response_max.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(response_max);\n});\n\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Type-Data", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"init_exam_type_data\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"IPSE - 4HR Single Exam\", weight: 40.2, id: find_exam(\"IPSE - 4HR Single Exam\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Get-Random", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_random_functions\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Room-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"room_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar roomSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"rooms\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"deleted\", \"office\", \"room_id\", \"room_name\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"rooms\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Room Schema\", function(){\n pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\",\n \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"notes\": {\"type\": \"string\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_email\": {\"type\": [\"null\", \"string\"]},\n \"exam_destroyed_date\": {\"type\": [\"null\", \"string\"]},\n \"receipt\": {\"type\": [\"null\", \"number\", \"string\"]},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\",\n \"office_number\", \"timezone\"]\n },\n \"exam_id\": {\"type\": \"number\"},\n \"bcmp_job_id\": {\"type\": [\"null\", \"number\"]},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"sbc_managed_ind\": {\"type\": \"number\"},\n \"invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"examinee_phone\": {\"type\": [\"null\", \"string\"]},\n \"upload_received_ind\": {\"type\": \"number\"},\n \"booking_id\": {\"type\": [\"null\", \"number\"]},\n \"booking\": {\"type\": [\"null\", \"object\"]},\n \"invigilator\": {\"type\": [\"null\", \"object\"]},\n \"is_pesticide\": {\"type\": \"number\"},\n \"exam_returned_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_email\": {\"type\": [\"null\", \"string\"]},\n \"candidates_list\": {\"type\": [\"null\", \"array\", \"object\"]},\n \"exam_returned_tracking_number\": {\"type\": [\"null\", \"string\"]},\n \"payee_name\": {\"type\": [\"null\", \"string\"]},\n \"deleted_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_phone\": {\"type\": [\"null\", \"string\"]},\n \"event_id\": {\"type\": \"string\"},\n \"payee_ind\": {\"type\": \"number\"},\n \"exam_received_date\": {\"type\": [\"null\", \"string\"]},\n \"number_of_students\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"offsite_location\": {\"type\": [\"null\", \"string\"]},\n \"exam_name\": {\"type\": \"string\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"receipt_sent_ind\": {\"type\": \"number\"}\n },\n \"required\": [\"exam_type\", \"office_id\", \"exam_type_id\", \"notes\", \"exam_written_ind\", \"examinee_email\", \"exam_destroyed_date\", \"receipt\",\n \"session_number\", \"office\", \"exam_id\", \"bcmp_job_id\", \"expiry_date\", \"sbc_managed_ind\", \"invigilator_id\", \"examinee_phone\",\n \"upload_received_ind\", \"booking_id\", \"booking\", \"invigilator\", \"is_pesticide\", \"exam_returned_date\", \"payee_email\",\n \"candidates_list\", \"exam_returned_tracking_number\", \"payee_name\", \"deleted_date\", \"payee_phone\", \"event_id\", \"payee_ind\",\n \"exam_received_date\", \"number_of_students\", \"exam_method\", \"offsite_location\", \"exam_name\", \"examinee_name\", \"receipt_sent_ind\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"exam\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\",\n \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"notes\": {\"type\": [\"null\", \"string\"]},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_email\": {\"type\": [\"null\", \"string\"]},\n \"exam_destroyed_date\": {\"type\": [\"null\", \"string\"]},\n \"receipt\": {\"type\": [\"null\", \"string\"]},\n \"session_number\": {\"type\": [\"number\", \"null\", \"string\"]},\n \"exam_id\": {\"type\": \"number\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"sbc_managed_ind\": {\"type\": \"number\"},\n \"invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"examinee_phone\": {\"type\": [\"null\", \"string\"]},\n \"upload_received_ind\": {\"type\": \"number\"},\n \"booking_id\": {\"type\": [\"null\", \"number\"]},\n \"booking\": {\"type\": [\"null\", \"object\"]},\n \"examinee_name\": {\"type\": [\"null\", \"string\"]},\n \"invigilator\": {\"type\": [\"null\", \"object\"]},\n \"is_pesticide\": {\"type\": \"number\"},\n \"exam_returned_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_email\": {\"type\": [\"null\", \"string\"]},\n \"candidates_list\": {\"type\": [\"null\", \"array\", \"object\"]},\n \"exam_returned_tracking_number\": {\"type\": [\"null\", \"string\"]},\n \"payee_name\": {\"type\": [\"null\", \"string\"]},\n \"deleted_date\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"payee_phone\": {\"type\": [\"null\", \"string\"]},\n \"event_id\": {\"type\": [\"null\", \"string\"]},\n \"payee_ind\": {\"type\": \"number\"},\n \"exam_received_date\": {\"type\": [\"null\", \"string\"]},\n \"number_of_students\": {\"type\": [\"null\", \"number\"]},\n \"exam_method\": {\"type\": \"string\"},\n \"offsite_location\": {\"type\": [\"null\", \"string\"]},\n \"receipt_sent_ind\": {\"type\": \"number\"},\n \"exam_name\": {\"type\": \"string\"},\n \"bcmp_job_id\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"exam_type\", \"office_id\", \"exam_type_id\", \"notes\", \"exam_written_ind\", \"examinee_email\", \"exam_destroyed_date\",\n \"receipt\", \"session_number\", \"exam_id\", \"expiry_date\", \"sbc_managed_ind\", \"invigilator_id\",\n \"examinee_phone\", \"upload_received_ind\", \"booking_id\", \"booking\", \"examinee_name\", \"invigilator\",\n \"is_pesticide\", \"exam_returned_date\", \"payee_email\", \"candidates_list\", \"exam_returned_tracking_number\",\n \"payee_name\", \"deleted_date\", \"office\", \"payee_phone\", \"event_id\", \"payee_ind\", \"exam_received_date\",\n \"number_of_students\", \"exam_method\", \"offsite_location\", \"receipt_sent_ind\", \"exam_name\", \"bcmp_job_id\"]\n },\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"exams\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_name\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"room_id\": {\"type\": [\"null\", \"number\"]},\n \"blackout_notes\": {\"type\": [\"null\", \"string\"]},\n \"shadow_invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"room\": {\n \"type\": [\"null\", \"object\"],\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"deleted\", \"room_id\", \"room_name\"]\n },\n \"booking_id\": {\"type\": \"number\"},\n \"end_time\": {\"type\": \"string\"},\n \"booking_contact_information\": {\"type\": [\"null\", \"string\"]},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"invigilators\": {\"type\": \"array\"},\n \"fees\": {\"type\": [\"null\", \"string\"]},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_name\", \"blackout_flag\", \"sbc_staff_invigilated\", \"office_id\", \"room_id\", \"blackout_notes\",\n \"shadow_invigilator_id\", \"room\", \"booking_id\", \"end_time\", \"booking_contact_information\", \"recurring_uuid\",\n \"office\", \"invigilators\", \"fees\", \"start_time\"]\n },\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Invigilator-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"invigilator_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar invigilatorSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"invigilators\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"contact_email\": {\"type\": \"string\"},\n \"contact_phone\": {\"type\": \"string\"},\n \"contract_expiry_date\": {\"type\": \"string\"},\n \"contract_number\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"invigilator_id\": {\"type\": \"number\"},\n \"invigilator_name\": {\"type\": \"string\"},\n \"invigilator_notes\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"shadow_count\": {\"type\": \"number\"},\n \"shadow_flag\": {\"type\": \"string\"}\n },\n \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\",\n \"deleted\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\",\n \"office\", \"office_id\", \"shadow_count\", \"shadow_flag\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"invigilators\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Invigilator Response Schema\", function(){\n pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Invig-One-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"invig_one_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar invigilatorSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"invigilator\": {\n \"type\": \"object\",\n \"properties\": {\n \"contact_email\": {\"type\": \"string\"},\n \"contact_phone\": {\"type\": \"string\"},\n \"contract_expiry_date\": {\"type\": \"string\"},\n \"contract_number\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"invigilator_id\": {\"type\": \"number\"},\n \"invigilator_name\": {\"type\": \"string\"},\n \"invigilator_notes\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"shadow_count\": {\"type\": \"number\"},\n \"shadow_flag\": {\"type\": \"string\"}\n },\n \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\",\n \"deleted\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\",\n \"office\", \"office_id\", \"shadow_count\", \"shadow_flag\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"invigilator\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Invigilator Response Schema\", function(){\n pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointment\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"online_flag\": {\"type\": \"boolean\"},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"services\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n }\n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\",\n \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\",\n \"start_time\", \"blackout_flag\", \"citizen_id\", \"online_flag\",\n \"recurring_uuid\", \"service\"],\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"appointment\", \"errors\"],\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointments Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an appointment property.\npm.test(\"Response should have appointment property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"appointment\")).to.be.true;\n});\n\nvar name = jsonData.appointment.citizen_name;\nvar comments = jsonData.appointment.comments;\nvar contact = jsonData.appointment.contact_information;\nvar name_expect = JSON.parse(pm.globals.get(\"appt_citizen_name\"));\nvar comments_expect = JSON.parse(pm.globals.get(\"appt_comments\"));\nvar contact_expect = JSON.parse(pm.globals.get(\"appt_contact_information\"));\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"appointment\")) {\n\n //Test to see if service id has expected value\n pm.test(\"Validate Service ID has expected value\", function(){\n pm.expect(jsonData.service_id === environment.service_id);\n });\n\n //Test to see if office id has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if category has expected value\n pm.test(\"Validate category has expected value\", function(){\n pm.expect(jsonData.category === environment.category);\n });\n\n //Test to see if citizen name field has expected value\n pm.test(\"Name should be '\" + name_expect + \"', it is '\" + name + \"'\", function(){\n pm.expect(name).to.be.eql(name_expect);\n });\n //Test to see if comments field has expected value\n pm.test(\"Comments should be '\" + comments_expect + \"'; it is '\" + comments +\"'\", function(){\n pm.expect(comments).to.be.eql(comments_expect);\n });\n //Test to see if contact info field has expected value\n pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact +\"'\", function(){\n pm.expect(contact).to.be.eql(contact_expect);\n });\n};\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_list_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointments\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": [\"null\", \"string\"]},\n \"contact_information\": {\"type\": [\"null\", \"string\"]},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"online_flag\": {\"type\": \"boolean\"},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"services\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n },\n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\",\n \"contact_information\", \"end_time\", \"office\", \"office_id\",\n \"service_id\", \"start_time\", \"blackout_flag\", \"citizen_id\",\n \"online_flag\", \"recurring_uuid\", \"service\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"appointments\"],\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointment List Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Make sure the CSR schema is OK.", - "eval(environment.csr_schema_check)", - "", - "//var jsonData = pm.response.json();", - "//pm.environment.set(\"csr_schema_check\", jsonData.data);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Room IDs", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.room_schema_check);", - "", - "if (jsonData.hasOwnProperty(\"rooms\")) {", - " room_id_1 = jsonData.rooms[0].room_id;", - " room_id_2 = room_id_1;", - " if (jsonData.rooms.length > 1) {", - " room_id_2 = jsonData.rooms[1].room_id;", - " }", - " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", - " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Service IDs", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data, check the schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.service_schema_check);", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", - " ", - " // Test the authenticate response.", - " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", - " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", - " });", - " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", - " pm.expect(element.actual_service_ind).to.be.eql(1);", - " });", - " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - Rural PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the service IDs.", - "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver Booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Set health response time variable.", - "max_response_time = 1500;", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"max_response_time\", JSON.stringify(max_response_time));", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is health'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_response_time) {", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint Booking\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint Booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is health'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint Booking\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint Booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints" - }, - { - "name": "Get Exam Types", - "item": [ - { - "name": "Exam Type List", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Define the JSON Schema expected in response", - "var examTypeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_types\": {", - " \"type\": \"array\",", - " \"items\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_color\": {\"type\": \"string\"},", - " \"exam_type_id\": {\"type\": \"number\"},", - " \"exam_type_name\": {\"type\": \"string\"},", - " \"group_exam_ind\": {\"type\": \"number\"},", - " \"ita_ind\": {\"type\": \"number\"},", - " \"method_type\": {\"type\": \"string\"},", - " \"number_of_hours\": {\"type\": \"number\"},", - " \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},", - " \"pesticide_exam_ind\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\",", - " \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]", - " }", - " },", - " \"errors\": {\"type\": [\"object\", \"string\"]}", - " },", - " \"required\": [\"exam_types\", \"errors\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Exam type response has valid schema\", function(){", - " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", - "});", - "", - "// Store all exam type IDs for future use in adding exams", - "var allExamIds = [];", - "", - "// Make sure some data returned.", - "pm.test(\"Response has exam_types property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", - "});", - "pm.test(\"Response has at least one exam_type\", function(){", - " pm.expect(jsonData.exam_types.length).to.be.above(0);", - "});", - "", - "// Set up list of valid exam types, create random functions.", - "eval(environment.init_exam_type_data);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exam_types/", - "host": [ - "{{url}}exam_types" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Exams", - "item": [ - { - "name": "Exam Post End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", - "", - "// Store other variables for later use.", - "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", - "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", - "", - "// Calculate an expiry date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format expiry date for the exam.", - "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "", - "// If jsonData has an exam property, save exam ID.", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam List End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_list_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Put End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = eventNumber;", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId.toString()));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam by ID", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam by Event", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "// Define the JSON Schema expected in response", - "var examSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"message\": {\"type\": \"boolean\"}", - " },", - " \"required\": [\"message\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Exam Schema\", function(){", - " pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;", - "});", - "", - "// Make sure the message is true.", - "pm.test(\"Result is '\" + jsonData.message.toString() + \"', it should be 'true'\", function() {", - " pm.expect(jsonData.message).to.be.eql(true);", - "});", - "", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Put the event ID in the right format.\r", - "var event_number = JSON.parse(postman.getEnvironmentVariable(\"update_id\"))\r", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/event_id/{{event_number}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "event_id", - "{{event_number}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Exams Export", - "item": [ - { - "name": "Exams Export List", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than 20,000ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(20000);", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date four weeks prior to today.", - "var start = new Date();", - "start.setDate(start.getDate()-28);", - "", - "// Get year, day, month from the start time.", - "start_year = start.getFullYear().toString();", - "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", - "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", - "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", - "", - "// Get year, day, month from the current day.", - "var today = new Date();", - "end_year = today.getFullYear().toString();", - "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", - "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", - "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", - "", - "console.log(\"Start: \" + start_date);", - "console.log(\"End: \" + end_date);", - "pm.globals.set(\"start_date\", start_date);", - "pm.globals.set(\"end_date\", end_date);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "export", - "" - ], - "query": [ - { - "key": "start_date", - "value": "{{start_date}}" - }, - { - "key": "end_date", - "value": "{{end_date}}" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Bookings", - "item": [ - { - "name": "Booking Post End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking List End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Put End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point Again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Booking Recurring", - "item": [ - { - "name": "Book first recurring event", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", - "pm.globals.set(\"booking_contact_information\", JSON.stringify(\"Postman Contact\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book second recurring event", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"second_event_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+8);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book third recurring event", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"third_event_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+9);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check first booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", - "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", - "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{second_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{second_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third booking", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", - "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{third_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{third_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update second event", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\n \"booking_name\": \"Second booking event updated\",\n \"booking_contact_information\": \"My_favourite_postman@gmail.com\"\n}" - }, - "url": { - "raw": "{{url}}bookings/{{second_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{second_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check first booking again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", - "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second booking after update", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = \"Second booking event updated\";", - "var contact_expect = \"My_favourite_postman@gmail.com\";", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{second_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{second_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third booking again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", - "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{third_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{third_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update all events", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "// Define the JSON Schema expected in response", - "var examSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"bookings\": {\"type\": \"object\"},", - " \"errors\": {\"type\": [\"object\", \"string\"]}", - " },", - " \"required\": [\"bookings\", \"errors\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Update all events schema\", function(){", - " pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Print out the recurring booking ID.", - "console.log(\"Recurring booking uuid:\");", - "console.log(pm.globals.get(\"pm_booking_uuid\"));", - "pm_url_uuid = JSON.parse(pm.globals.get(\"pm_booking_uuid\"))", - "console.log(pm_url_uuid);", - "pm.globals.set(\"pm_url_uuid\", pm_url_uuid);", - " ", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\n \"booking_name\": \"All recurring pm events #2\",\n \"booking_contact_information\": \"My_second_favourite_postman@gmail.com\",\n \"invigilator_id\": []\n}" - }, - "url": { - "raw": "{{url}}bookings/recurring/{{pm_url_uuid}}", - "host": [ - "{{url}}bookings" - ], - "path": [ - "recurring", - "{{pm_url_uuid}}" - ] - } - }, - "response": [] - }, - { - "name": "Check first booking update all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = \"All recurring pm events #2\";", - "var contact_expect = \"My_second_favourite_postman@gmail.com\";", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second booking update all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = \"All recurring pm events #2\";", - "var contact_expect = \"My_second_favourite_postman@gmail.com\";", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{second_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{second_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third booking update all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "// Get booking data.", - "var name = jsonData.booking.booking_name;", - "var contact = jsonData.booking.booking_contact_information;", - "var name_expect = \"All recurring pm events #2\";", - "var contact_expect = \"My_second_favourite_postman@gmail.com\";", - "", - "// Make sure data for the first booking hasn't changed.", - "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", - " pm.expect(name).to.be.eql(name_expect);", - "});", - "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", - " pm.expect(contact).to.be.eql(contact_expect);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{third_event_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{third_event_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/recurring/{{pm_url_uuid}}", - "host": [ - "{{url}}bookings" - ], - "path": [ - "recurring", - "{{pm_url_uuid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Invigilators checks", - "item": [ - { - "name": "Invigilators", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.invigilator_schema_check);", - "", - "// Make sure there is at least one invigilator.", - "invigilators = jsonData.invigilators;", - "pm.test(\"Number of invigilators is \" + invigilators.length.toString() + \", must not be 0\", function() {", - " pm.expect(invigilators.length).to.not.be.eql(0);", - "})", - "", - "// Get the first invigilator.", - "first = invigilators[0];", - "", - "// Make sure the invigilator shadow count is 2, and the flag is \"Y\"", - "pm.test(\"Invigilator shadow count is \" + first.shadow_count.toString() + \", must be 2\", function() {", - " pm.expect(first.shadow_count).to.be.eql(2);", - "})", - "pm.test(\"Invigilator shadow flag is \" + first.shadow_flag.toString() + \", must be 'Y'\", function() {", - " pm.expect(first.shadow_flag).to.be.eql(\"Y\");", - "})", - "", - "// Save some values of the first invigilator.", - "invId = first.invigilator_id;", - "count = first.shadow_count;", - "flag = first.shadow_flag;", - "postman.setEnvironmentVariable(\"inv_id\", invId);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}invigilators/", - "host": [ - "{{url}}invigilators" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Invigilators offsite", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body, test the schema.", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.invigilator_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}invigilators/offsite/", - "host": [ - "{{url}}invigilators" - ], - "path": [ - "offsite", - "" - ] - } - }, - "response": [] - }, - { - "name": "Invigilator subtract", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.invig_one_schema_check);", - "", - "first = jsonData.invigilator;", - "", - "// Save some values.", - "newId = first.invigilator_id;", - "newEmail = first.contact_email;", - "newPhone = first.contact_phone;", - "newNotes = first.invigilator_notes;", - "newCount = first.shadow_count;", - "newFlag = first.shadow_flag;", - "", - "// Temporary debugging.", - "console.log(\"==> New invigilator data:\");", - "console.log(\" --> id: \" + newId);", - "console.log(\" --> email: \" + newEmail);", - "console.log(\" --> phone: \" + newPhone);", - "console.log(\" --> notes: \" + newNotes);", - "console.log(\" --> count: \" + newCount);", - "console.log(\" --> flag: \" + newFlag);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "", - "value": "", - "type": "text", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}invigilator/{{inv_id}}/?subtract=True&add=False", - "host": [ - "{{url}}invigilator" - ], - "path": [ - "{{inv_id}}", - "" - ], - "query": [ - { - "key": "subtract", - "value": "True" - }, - { - "key": "add", - "value": "False" - } - ] - } - }, - "response": [] - }, - { - "name": "Invigilator add", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.invig_one_schema_check);", - "", - "first = jsonData.invigilator;", - "", - "// Save some values.", - "newId = first.invigilator_id;", - "newEmail = first.contact_email;", - "newPhone = first.contact_phone;", - "newNotes = first.invigilator_notes;", - "newCount = first.shadow_count;", - "newFlag = first.shadow_flag;", - "", - "// Temporary debugging.", - "console.log(\"==> New invigilator data:\");", - "console.log(\" --> id: \" + newId);", - "console.log(\" --> email: \" + newEmail);", - "console.log(\" --> phone: \" + newPhone);", - "console.log(\" --> notes: \" + newNotes);", - "console.log(\" --> count: \" + newCount);", - "console.log(\" --> flag: \" + newFlag);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "", - "value": "", - "type": "text", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}invigilator/{{inv_id}}/?subtract=False&add=True", - "host": [ - "{{url}}invigilator" - ], - "path": [ - "{{inv_id}}", - "" - ], - "query": [ - { - "key": "subtract", - "value": "False" - }, - { - "key": "add", - "value": "True" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Rooms", - "item": [ - { - "name": "Room List End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.room_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "CSRS", - "item": [ - { - "name": "CSRS Me Get End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.csr_schema_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Offices", - "item": [ - { - "name": "Office List End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "eval(environment.all_office_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}offices/", - "host": [ - "{{url}}offices" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Office Available Timeslots", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}offices/{{current_office_id}}/slots/", - "host": [ - "{{url}}offices" - ], - "path": [ - "{{current_office_id}}", - "slots", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Appointments", - "item": [ - { - "name": "Appointment Post End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"My contact info\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment List End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid\\", - "eval(environment.appointment_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Put End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"super EARLY\"));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}}\n}" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}?office_id={{current_office_id}}", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ], - "query": [ - { - "key": "office_id", - "value": "{{current_office_id}}" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Appointments Recurring", - "item": [ - { - "name": "Book first appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT PERIOD\"));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"Office needs a break\"));", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Contact info, me@me.com\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book second appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"second_appt_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+8);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book third appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"third_appt_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+9);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check first appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{second_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{second_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{third_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{third_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update second appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT UPDATE 2nd\"));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"Update blackout comments\"));", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Update blackout contact\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}}\n}" - }, - "url": { - "raw": "{{url}}appointments/{{second_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{second_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second appointment again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{second_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{second_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check first appointment again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Reset variables back to their first and third appt values.\r", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT PERIOD\"));\r", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"Office needs a break\"));\r", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Contact info, me@me.com\"));\r", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third appointment again", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{third_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{third_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update all appointments", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "// Define the JSON Schema expected in response", - "var apptAllSchema = {", - " \"type\": \"object\", ", - " \"properties\": {", - " \"appointments\": {\"type\": \"object\"},", - " \"errors\": {\"type\": [\"object\", \"string\"]}", - " },", - " \"required\": [\"appointments\", \"errors\"],", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Recurring Appointments Schema\", function(){", - " pm.expect(tv4.validate(jsonData, apptAllSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Print out the appointment ID.", - "pm_url_uuid = JSON.parse(pm.globals.get(\"pm_appt_uuid\"))", - "pm.globals.set(\"pm_url_uuid\", pm_url_uuid);", - "", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT ALL UPDATE\"));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"All blackout comments\"));", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"All Update blackout contacts\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}}\n}" - }, - "url": { - "raw": "{{url}}appointments/recurring/{{pm_url_uuid}}", - "host": [ - "{{url}}appointments" - ], - "path": [ - "recurring", - "{{pm_url_uuid}}" - ] - } - }, - "response": [] - }, - { - "name": "Check first appointment all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check second appointment all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{second_appt_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{second_appt_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Check third appointment all", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/recurring/{{pm_url_uuid}}", - "host": [ - "{{url}}appointments" - ], - "path": [ - "recurring", - "{{pm_url_uuid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Public User Appointments", - "item": [ - { - "name": "Authenticate and create user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "var allSchema = {", - " \"type\": \"array\",", - " \"items\": {\"type\": \"object\"}", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate All Users Schema\", function(){", - " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", - "});", - "", - "var firstUser = jsonData[0];", - "console.log(firstUser)", - "", - "var userSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"email\": {\"type\": \"string\"},", - " \"last_name\": {\"type\": \"string\"},", - " \"user_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"},", - " \"send_email_reminders\": {\"type\": \"boolean\"},", - " \"send_sms_reminders\": {\"type\": \"boolean\"},", - " \"display_name\": {\"type\": \"string\"},", - " \"telephone\": {\"type\": \"string\"}", - " },", - " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", - " \"display_name\", \"telephone\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate First User Schema\", function(){", - " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", - "});", - "", - "//Dynamic variables used for end-point testing later on", - "var user_id = firstUser.user_id;", - "var user_phone = firstUser.telephone;", - "postman.setEnvironmentVariable(\"user_id\", JSON.stringify(user_id));", - "postman.setEnvironmentVariable(\"user_phone\", JSON.stringify(user_phone));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Typea", - "type": "text", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{public_user_id}}&password={{public_user_password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{public_url}}users/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book an appointment", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "", - "// Update the comments to include phone number, before data test.", - "var comments_old = JSON.parse(pm.globals.get(\"appt_comments\"));", - "var user_phone = JSON.parse(postman.getEnvironmentVariable(\"user_phone\"));", - "var comments_new = comments_old + '. Phone: ' + user_phone;", - "console.log(\"==> Old: \" + comments_old);", - "console.log(\"==> Phone: \" + user_phone);", - "console.log(\"==> New: \" + comments_new);", - "pm.globals.set(\"appt_comments\", JSON.stringify(comments_new));", - "", - "// Now ready for the data check.", - "eval(environment.appointment_data_check);", - "", - "//Dynamic variable used for end-point testing later on", - "var appointment_id = jsonData.appointment.appointment_id;", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));", - "", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "var day = later.getDay();", - "later.setDate(later.getDate() + 5 + (day === 6 ? 2 : +!day) + (Math.floor((5 - 1 + (day % 6 || 1)) / 5) * 2));", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:30:00Z\";", - "", - "pm.globals.set(\"public_start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"public_end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "pm.globals.set(\"appt_comments\", JSON.stringify(\"My self serve appt.\"));", - "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"cfms-postman-public-user\"));", - "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"test@test.com\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{public_start_time}},\n \"end_time\": {{public_end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" - }, - "url": { - "raw": "{{public_url}}appointments/", - "host": [ - "{{public_url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Edit user profile", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "var allSchema = {", - " \"type\": \"array\",", - " \"items\": {\"type\": \"object\"}", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate All Users Schema\", function(){", - " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", - "});", - "", - "var firstUser = jsonData[0];", - "", - "var userSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"email\": {\"type\": \"string\"},", - " \"last_name\": {\"type\": \"string\"},", - " \"user_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"},", - " \"send_email_reminders\": {\"type\": \"boolean\"},", - " \"send_sms_reminders\": {\"type\": \"boolean\"},", - " \"display_name\": {\"type\": \"string\"},", - " \"telephone\": {\"type\": \"string\"}", - " },", - " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", - " \"display_name\", \"telephone\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate First User Schema\", function(){", - " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"test@test.com\",\n \"telephone\": \"0000000000\",\n \"send_email_reminders\": true,\n \"send_sms_reminders\": true\n}" - }, - "url": { - "raw": "{{public_url}}users/{{user_id}}/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "{{user_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Get user profile", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "var allSchema = {", - " \"type\": \"array\",", - " \"items\": {\"type\": \"object\"}", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate All Users Schema\", function(){", - " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", - "});", - "", - "var firstUser = jsonData[0];", - "", - "var userSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"email\": {\"type\": \"string\"},", - " \"last_name\": {\"type\": \"string\"},", - " \"user_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"},", - " \"send_email_reminders\": {\"type\": \"boolean\"},", - " \"send_sms_reminders\": {\"type\": \"boolean\"},", - " \"display_name\": {\"type\": \"string\"},", - " \"telephone\": {\"type\": \"string\"}", - " },", - " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", - " \"display_name\", \"telephone\"]", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate First User Schema\", function(){", - " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [ - { - "key": "", - "type": "text", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{public_url}}users/me/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - }, - { - "name": "List all appointments", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid\\", - "eval(environment.appointment_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{public_url}}users/appointments/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "appointments", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{public_url}}appointments/{{appointment_id}}/", - "host": [ - "{{public_url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "auth_url = globals.auth_url;", - "realm = globals.realm;", - "clientid = globals.clientid;", - "userid = globals.public_user_id;", - "password = globals.public_user_password;", - "client_secret = globals.client_secret;", - "", - "const echoPostRequest = {", - " url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',", - " method: 'POST',", - " header: 'Content-Type:application/x-www-form-urlencoded',", - " body: {", - " mode: 'raw',", - " raw: 'grant_type=password&client_id=' + clientid ", - " + '&username=' + userid ", - " + '&password=' + password", - " + '&client_secret=' + client_secret", - " }", - "};", - "pm.sendRequest(echoPostRequest, function (err, res) {", - " var jsonData = res.json();", - " if (jsonData.hasOwnProperty('access_token')) {", - " \tpm.globals.set(\"public_user_token\", jsonData.access_token);", - "\t pm.globals.set(\"public_user_refresh_token\", jsonData.refresh_token);", - "\t if (err) {", - "\t console.log(err);", - "\t }", - "\t // console.log(err ? err : res.json());", - "\t} else {", - "\t pm.globals.set(\"public_user_token\", 0);", - "\t pm.globals.set(\"public_user_refresh_token\", 0);", - "\t}", - "});" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] - } - ] -} \ No newline at end of file + "info": { + "_postman_id": "614e41a1-da60-4f20-be17-c5fd6b52c6d2", + "name": "API_Test_TheQ_Booking Copy6-9", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Setup TheQ", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "", + " pm.globals.set('public_url', pm.globals.get(usePrefix + 'public_url'));", + " pm.globals.set('public_user_id', pm.globals.get(usePrefix + 'public_user_id'));", + " pm.globals.set('public_user_password', pm.globals.get(usePrefix + 'public_user_password'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "console.log(\" --> public_url: \" + pm.globals.get(\"public_url\"));", + "console.log(\" --> public_user_id: \" + pm.globals.get(\"public_user_id\"));", + "console.log(\" --> public_user_password: \" + pm.globals.get(\"public_user_password\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"qtxn_id_number\");", + "pm.environment.unset(\"counter_id_number\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Complex-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"complex_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Create-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.expect(pm.response.code).to.eql(201);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 201) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 201 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Citizen-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"citizen_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var citizenSchema = {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": [\"null\", \"string\"]},\n \"citizen_name\": {\"type\": [\"null\", \"string\"]},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": [\"null\", \"number\"]},\n \"service_reqs\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\": {\"type\": \"number\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"periods\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"},\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]\n }\n },\n \"quantity\": {\"type\": \"number\"},\n \"service\": {\n \"type\": \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"},\n },\n \"required\": [\"external_service_name\", \"online_availability\", \"online_link\", \"parent\", \"parent_id\", \"service_name\"]\n },\n \"service_id\": {\"type\": \"number\"},\n \"sr_id\": {\"type\": \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n }\n },\n \"required\": [\"channel\", \"channel_id\", \"citizen_id\", \"periods\", \"quantity\", \"service\", \"service_id\",\n \"sr_id\", \"sr_number\", \"sr_state\"]\n }\n },\n \"user\": {\"type\": [\"null\", \"object\"]},\n \"citizen_id\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": [\"null\", \"number\"]},\n \"counter\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_comments\": {\"type\": [\"null\", \"string\"]},\n \"priority\": {\"type\": \"number\"},\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n },\n \"required\": [\"accurate_time_ind\", \"ticket_number\", \"citizen_name\", \"qt_xn_citizen_ind\", \"user_id\",\n \"service_reqs\", \"citizen_id\", \"counter_id\", \"start_time\",\n \"citizen_comments\", \"priority\", \"cs\", \"office_id\"]\n },\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Citizen Schema\", function(){\n pm.expect(tv4.validate(allElements, citizenSchema)).to.be.true;\n});\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n // tests[testTitle + \"conforms to schema\"] = tv4.validate(element, citizenSchema);\n\n //Test to see if response schema is valid\n pm.test(testTitle + \"conforms to schema\", function(){\n pm.expect(tv4.validate(allElements, citizenSchema)).to.be.true;\n });\n\n\n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Service-Schema-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"service_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the schema.\nvar serviceSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"data\": {\n \n \"properties\": {\n \"services\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"services\", \"errors\"],\n}\n}\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Service Schema\", function(){\n pm.expect(tv4.validate(jsonData, serviceSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Service-Request-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"service_request_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var schema = {\n \"properties\" : {\n \"channel\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"challen_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\" : {\"type\" : \"number\"},\n \"citizen\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": \"string\"},\n \"citizen_name\": {\"type\": \"string\"},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": \"null\"},\n \"user\": {\"type\": \"null\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"counter\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_comments\": {\"type\": \"string\"},\n \"priority\": {\"type\": \"number\"},\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n },\n \"required\": [\"accurate_time_ind\", \"ticket_number\", \"citizen_name\", \"qt_xn_citizen_ind\",\n \"user_id\", \"citizen_id\", \"counter_id\", \"start_time\",\n \"citizen_comments\", \"priority\", \"cs\", \"office_id\"]\n },\n \"citizen_id\" : {\"type\" : \"number\"},\n \"periods\" : {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"},\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\",\n \"time_end\", \"time_start\"]\n }\n },\n \"quantity\" : {\"type\" : \"number\"},\n \"service\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"},\n },\n \"required\": [\"external_service_name\", \"online_availability\", \"online_link\",\n \"parent\", \"parent_id\", \"service_name\"]\n },\n \"service_id\" : {\"type\" : \"number\"},\n \"sr_id\" : {\"type\" : \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n }\n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-One-Service-Request-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"one_service_request_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var serviceRequestSchema = {\n \"type\": \"object\",\n \"properties\" : {\n \"service_request\": {\n \"type\": \"object\",\n \"properties\": {\n \"channel\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"channel_name\": {\"type\": \"string\"}\n },\n \"required\": [\"channel_name\"]\n },\n \"channel_id\" : {\"type\" : \"number\"},\n \"citizen\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"cs\": {\n \"type\": \"object\",\n \"properties\": {\n \"cs_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"cs_state_name\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"priority\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"citizen_name\": {\"type\": [\"null\", \"string\"]},\n \"qt_xn_citizen_ind\": {\"type\": \"number\"},\n \"user_id\": {\"type\": [\"null\", \"number\"]},\n \"counter_id\": {\"type\": \"number\"},\n \"counter\": {\"type\": \"number\"},\n \"citizen_comments\": {\"type\": \"string\"},\n \"accurate_time_ind\": {\"type\": \"number\"},\n \"ticket_number\": {\"type\": [\"null\", \"string\"]},\n \"user\": {\"type\": [\"null\", \"object\"]}\n },\n \"required\": [\"cs\", \"office_id\", \"priority\", \"start_time\", \"citizen_id\", \"citizen_name\", \"qt_xn_citizen_ind\",\n \"user_id\", \"counter_id\", \"citizen_comments\", \"accurate_time_ind\", \"ticket_number\"]\n },\n \"citizen_id\" : {\"type\" : \"number\"},\n \"periods\" : {\n \"type\" : \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"qt_xn_csr_ind\", \"username\"]\n },\n \"csr_id\": {\"type\": \"number\"},\n \"period_id\": {\"type\": \"number\"},\n \"ps\": {\n \"type\": \"object\",\n \"properties\": {\n \"ps_name\": {\"type\": \"string\"}\n },\n \"required\": [\"ps_name\"]\n },\n \"ps_id\": {\"type\": \"number\"},\n \"time_end\": {\"type\": [\"null\", \"string\"]},\n \"time_start\": {\"type\": \"string\"}\n },\n \"required\": [\"csr\", \"csr_id\", \"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]\n }\n },\n \"quantity\" : {\"type\" : \"number\"},\n \"service\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n }\n }\n },\n \"service_id\" : {\"type\" : \"number\"},\n \"sr_id\" : {\"type\" : \"number\"},\n \"sr_number\": {\"type\": \"number\"},\n \"sr_state\" : {\n \"type\" : \"object\",\n \"properties\": {\n \"sr_code\": {\"type\": \"string\"}\n },\n \"required\": [\"sr_code\"]\n },\n },\n \"required\" : [\"channel\", \"channel_id\", \"citizen\", \"citizen_id\", \"periods\",\n \"quantity\", \"service\", \"service_id\", \"sr_id\", \"sr_number\", \"sr_state\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"service_request\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Service Request Schema\", function(){\n pm.expect(tv4.validate(jsonData, serviceRequestSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Get-Active-Citizens-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-CSR-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"csr_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar csrSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n },\n \"attention_needed\": {\"type\": \"boolean\"},\n \"active_citizens\": {\"type\": \"array\"},\n \"back_office_display\": {\"type\": \"string\"},\n \"recurring_feature_flag\": {\"type\": \"string\"},\n \"errors\": {}\n },\n \"required\": [\"csr\", \"attention_needed\", \"active_citizens\",\n \"back_office_display\", \"recurring_feature_flag\", \"errors\"],\n};\n\n//Test to see if response schema is valid" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-One-CSR-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"onecsr_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar csrSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csr\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n },\n \"errors\" : {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"csr\", \"errors\"]\n};\n\n//Test to see if response schema is valid" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Many-CSRs-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"manycsrs_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar manyCsrsSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"csrs\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter\": {\"type\": \"number\"},\n \"counter_id\": {\"type\": \"number\"},\n \"csr_id\": {\"type\": \"number\"},\n \"csr_state\": {\n \"type\": \"object\",\n \"properties\": {\n \"csr_state_desc\": {\"type\": \"string\"},\n \"csr_state_id\": {\"type\": \"number\"},\n \"csr_state_name\": {\"type\": \"string\"}\n },\n \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]\n },\n \"csr_state_id\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"finance_designate\": {\"type\": \"number\"},\n \"ita2_designate\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_manager\": {\"type\": \"number\"},\n \"pesticide_designate\": {\"type\": \"number\"},\n \"qt_xn_csr_ind\": {\"type\": \"number\"},\n \"receptionist_ind\": {\"type\": \"number\"},\n \"role\": {\n \"type\": \"object\",\n \"properties\": {\n \"role_code\": {\"type\": \"string\"},\n \"role_desc\": {\"type\": \"string\"},\n \"role_id\": {\"type\": \"number\"}\n },\n \"required\": [\"role_code\", \"role_desc\", \"role_id\"]\n },\n \"role_id\": {\"type\": \"number\"},\n \"username\": {\"type\": \"string\"}\n },\n \"required\": [\"counter\", \"counter_id\", \"csr_id\", \"csr_state\",\n \"csr_state_id\", \"deleted\", \"finance_designate\",\n \"ita2_designate\", \"office_id\", \"office_manager\",\n \"pesticide_designate\", \"qt_xn_csr_ind\", \"receptionist_ind\",\n \"role\", \"role_id\", \"username\"]\n }\n }\n }\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate many CSRs Schema\", function(){\n pm.expect(tv4.validate(jsonData, manyCsrsSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Office-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"office_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar officeSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate the Office Schema\", function(){\n pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-All-Offices-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"all_office_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar allOfficeSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"offices\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"max_person_appointment_per_day\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"appointment_days_limit\": {\"type\": \"number\"},\n \"online_status:\": {\"type\": \"string\"},\n \"sb_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n \"office_number\": {\"type\": \"number\"},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n }\n },\n \"back_office_list\": {\"type\": \"array\"},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"appointment_duration\": {\"type\": \"number\"},\n \"quick_list\": {\"type\": \"array\"},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": \"array\"},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\", \"office\", \"start_time\"]\n }\n }\n },\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"offices\", \"errors\"]\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Video-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"video_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar videoSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"videofiles\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\"type\": \"string\"},\n \"date\": {\"type\": \"string\"},\n \"size\": {\"type\": \"string\"}\n },\n \"required\": [\"name\", \"date\", \"size\"]\n }\n },\n \"manifest\": {\"type\": \"string\"},\n \"errors\": {\"type\": \"string\"},\n \"code\": {\"type\": \"number\"},\n \"space\": {\n \"type\": \"object\",\n \"properties\": {\n \"total\": {\"type\": \"number\"},\n \"used\": {\"type\": \"number\"},\n \"freespace\": {\"type\": \"number\"}\n },\n \"required\": [\"total\", \"used\", \"freespace\"]\n }\n },\n \"required\": [\"videofiles\", \"manifest\", \"errors\", \"code\", \"space\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate the Videofiles Schema\", function(){\n pm.expect(tv4.validate(jsonData, videoSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + } + ], + "description": "This folder performs basic authentication features." + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver TheQ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Set health response time variable.", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Get the maximum load time allowed.", + "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints" + }, + { + "name": "Check user login", + "item": [ + { + "name": "Authenticate default QTxn user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "description": "Make sure the operator ID can log in" + }, + "response": [] + }, + { + "name": "Authenticate non-QTxn user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid_nonqtxn\",\"password_nonqtxn\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"nonqtxn_token\", jsonData.access_token);", + "pm.globals.set(\"nonqtxn_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid_nonqtxn}}&password={{password_nonqtxn}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ] + } + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.csr_schema_check)", + "", + "// Make sure the postman operator is a GA.", + "if (jsonData.hasOwnProperty(\"csr\")) {", + " role = jsonData.csr.role.role_code;", + "}", + "else {", + " role = \"Unknown\"", + "};", + "", + "pm.test(\"The cfms-postman-operator role is \" + role + \", must be GA\", function() {", + " pm.expect(role).to.be.eql(\"GA\")", + "});", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check channels", + "item": [ + { + "name": "Get channels", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"channel_name\" : {", + " \"type\" : \"string\"", + " },", + " \"channel_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " }", + " },", + " \"required\" : [\"channel_name\", \"channel_id\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allChannels = jsonData.channels;", + "var channelCount = 0;", + "var phoneId = 0;", + "var emailId = 0;", + "var phoneText = \"Phone\";", + "var emailText = \"Email/Fax/Mail\";", + "allChannels.forEach(function(channel) {", + " channelCount ++;", + " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", + " tests[testTitle] = tv4.validate(channel, schema);", + " if (channel.channel_name === phoneText) {", + " phoneId = channel.channel_id;", + " }", + " if (channel.channel_name === emailText) {", + " emailId = channel.channel_id;", + " }", + "});", + "", + "// Check that you found the phone ID.", + "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", + " pm.expect(phoneId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the email ID.", + "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", + " pm.expect(emailId).to.not.be.eql(0);", + "});", + "", + "// Store this ID for future use.", + "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", + "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}channels/", + "host": [ + "{{url}}channels" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check counters", + "item": [ + { + "name": "Store CSR and Office Info", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.csr_schema_check)", + "", + "// Make sure that jsonData has an csr property.", + "pm.test(\"Response should have csr property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", + "});", + "", + "var csr = 0;", + "var office = 0;", + "var counters = 0;", + "var counter_text = \"Counter\";", + "var counter_id = 0;", + "var qtxn_text = \"Quick Trans\";", + "var qtxn_id = 0;", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", + " ", + " csr = jsonData.csr;", + " counter_id = 0;", + " qtxn_id = 0;", + "", + " // Make sure that jsonData has an booking property.", + " pm.test(\"CSR should have office property\", function(){", + " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", + " });", + " ", + " // Make sure office has counter property.", + " if (csr.hasOwnProperty(\"office\")) {", + " office = csr.office;", + " ", + " // Make sure that jsonData has an booking property.", + " pm.test(\"Office should have counters property\", function(){", + " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", + " });", + "", + " // Make sure office has counter property.", + " if (office.hasOwnProperty(\"counters\")) {", + " counters = office.counters;", + " ", + " // Search for Counter and Quick Trans counters", + " counters.forEach(function(counter) {", + " if (counter.counter_name === counter_text) {", + " counter_id = counter.counter_id;", + " }", + " if (counter.counter_name === qtxn_text) {", + " qtxn_id = counter.counter_id;", + " }", + " });", + " ", + " // Make sure you found the right IDs.", + " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", + " pm.expect(counter_id).to.not.be.eql(0);", + " });", + " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", + " pm.expect(qtxn_id).to.not.be.eql(0);", + " });", + " ", + " // Store the ids for future use.", + " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", + " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", + " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", + " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", + " }", + "", + " ", + " }", + "", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check categories", + "item": [ + { + "name": "Get categories", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"actual_service_ind\" : { \"type\" : \"number\" },", + " \"deleted\" : { \"type\" : [\"string\", \"null\"] },", + " \"display_dashboard_ind\" : { \"type\" : \"number\" },", + " \"external_service_name\": {\"type\": [\"null\", \"string\"]},", + " \"online_availability\" : {\"type\": [\"null\", \"string\"]},", + " \"online_link\": {\"type\": [\"null\", \"string\"]},", + " \"parent\": {\"type\": \"null\"},", + " \"parent_id\": {\"type\": \"null\"},", + " \"prefix\" : {\"type\" : \"string\"},", + " \"service_code\" : {\"type\" : \"string\"},", + " \"service_desc\" : {\"type\" : \"string\"},", + " \"service_id\" : {\"type\" : [\"number\", \"object\"]},", + " \"service_name\" : {\"type\" : \"string\"},", + " \"timeslot_duration\": {\"type\": [\"null\", \"number\"]}", + " },", + " \"required\" : [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"external_service_name\",", + " \"online_availability\", \"online_link\", \"parent\", \"parent_id\", \"prefix\", \"service_code\",", + " \"service_desc\", \"service_id\", \"service_name\",", + " \"timeslot_duration\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allCategories = jsonData.categories;", + "var categoryCount = 0;", + "allCategories.forEach(function(category) {", + " categoryCount ++;", + " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", + " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", + " var displayInd = category.display_dashboard_ind;", + " var serviceInd = category.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.eql(0);", + " });", + "", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(0);", + " });", + " ", + " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", + " pm.expect(category.parent_id).to.be.null;", + " });", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}categories/", + "host": [ + "{{url}}categories" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check services", + "item": [ + { + "name": "Get services", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.service_schema_check);", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", + " displayInd = element.display_dashboard_ind;", + " serviceInd = element.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.within(0, 1);", + " });", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(1);", + " });", + " ", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - Rural PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the MSP service.", + "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the Property Tax service.", + "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update quick lists", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.office_schema_check)", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}services/refresh/?office_id={{current_office_id}}", + "host": [ + "{{url}}services" + ], + "path": [ + "refresh", + "" + ], + "query": [ + { + "key": "office_id", + "value": "{{current_office_id}}" + } + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check smartboard", + "item": [ + { + "name": "Get smartboard", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Define the JSON Schema expected in response", + "var smartboardSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_type\": {\"type\": \"string\"},", + " \"citizens\": {", + " \"type\": \"array\",", + " \"items\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"ticket_number\": {\"type\": \"string\"},", + " \"active_period\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"period_id\": {\"type\": \"number\"},", + " \"ps\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"ps_name\": \"string\"", + " },", + " \"required\": [\"ps_name\"]", + " },", + " \"ps_id\": {\"type\": \"number\"},", + " \"time_end\": {\"type\": [\"null\", \"string\"]},", + " \"time_start\": {\"type\": \"string\"}", + " },", + " \"required\": [\"period_id\", \"ps\", \"ps_id\", \"time_end\", \"time_start\"]", + " },", + " },", + " \"required\": [\"ticket_number\", \"active_period\"]", + " },", + " },", + " },", + " \"required\": [\"office_type\", \"citizens\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate the Smartboard Schema\", function(){", + " pm.expect(tv4.validate(jsonData, smartboardSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}smartboard/?office_number={{current_office_number}}", + "host": [ + "{{url}}smartboard" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check videofiles", + "item": [ + { + "name": "Get videofiles", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.video_schema_check)", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}videofiles/", + "host": [ + "{{url}}videofiles" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Get videofiles for office", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.video_schema_check)", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}videofiles/?office_number={{current_office_number}}", + "host": [ + "{{url}}videofiles" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check CSRs and States", + "item": [ + { + "name": "Get CSRs", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, make sure schema is OK.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.manycsrs_schema_check)", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}csrs/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check Postman Finance No", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.onecsr_schema_check);", + "", + "pm.test(\"Response should include csr object\", function() {", + " pm.expect(jsonData).to.have.property(\"csr\");", + " pm.expect(jsonData.csr).to.be.an(\"object\");", + "});", + "", + "if (!jsonData.csr) {", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Response missing csr object\");", + "}", + "", + "// Make sure the CSR finance designation is correct.", + "var finance = jsonData.csr.finance_designate;", + "pm.test(\"Finance designate is \" + finance.toString() + \" - it should be 0\", function() {", + " pm.expect(finance).to.be.eql(0);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"finance_designate\": 0\r\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check Postman Finance Yes", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.onecsr_schema_check);", + "", + "pm.test(\"Response should include csr object\", function() {", + " pm.expect(jsonData).to.have.property(\"csr\");", + " pm.expect(jsonData.csr).to.be.an(\"object\");", + "});", + "", + "if (!jsonData.csr) {", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Response missing csr object\");", + "}", + "", + "// Make sure the CSR finance designation is correct.", + "var finance = jsonData.csr.finance_designate;", + "pm.test(\"Finance designate is \" + finance.toString() + \" - it should be 1\", function() {", + " pm.expect(finance).to.be.eql(1);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"finance_designate\": 1\r\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Get CSR states", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Define the JSON Schema expected in response", + "var csrStateSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_states\" : {", + " \"type\": \"array\",", + " \"items\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"},", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " }", + " },", + " \"errors\": {\"type\": [\"object\", \"string\"]}", + " },", + " \"required\": [\"csr_states\", \"errors\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate CSR States Schema\", function(){", + " pm.expect(tv4.validate(jsonData, csrStateSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}csr_states/", + "host": [ + "{{url}}csr_states" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Clear queue for tests", + "item": [ + { + "name": "Delete citizen queue driver", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Delete citizens, if there are any.", + " if (citizenIds.length > 0) {", + " ", + " // Set the current_client, to be deleted.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(citizenIds));", + " ", + " if (currentCitizen.service_reqs.length === 0) {", + " postman.setNextRequest(\"Next citizen left\");", + " // // Temporary kludge. Citizen left not working, so add SR, then delete.", + " // postman.setNextRequest(\"Temporary add MSP service request\");", + " }", + " else {", + " postman.setNextRequest(\"Next citizen finish service\");", + " }", + " }", + " ", + " // No more citizens. Clear the current, queue variables.", + " else {", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(\"\"));", + " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(\"\"));", + " postman.setNextRequest(\"End clear queue via healthz endpoint\");", + " }", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Next citizen finish service", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "var citizenToBeDeleted = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "var citizenData = jsonData.citizen;", + "var testTitle = \"Check citizen finish service\";", + "", + "// Make sure the response is valid.", + "pm.test(testTitle + \": Response should have property 'citizen'\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"citizen\")).to.be.true;", + "});", + "pm.test(testTitle + \": Response should not have property 'message' indicating an error\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"message\")).to.be.false;", + "});", + "pm.test(testTitle + \": Citizen marked as finished should be citizen \" + citizenToBeDeleted.toString(), function(){", + " pm.expect(citizenData.citizen_id).to.be.eql(citizenToBeDeleted);", + "});", + "", + "// Go back to the delete citizen queue driver.", + "postman.setNextRequest(\"Delete citizen queue driver\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Next citizen citizen left", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform checks.", + " pm.test(\"Check there are no citizens waiting\", function(){", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test(\"Citizen that left must be \" + currentCitizen.citizen_id.toString() + \" (is \" + currentCitizenId.toString(), function(){", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + "}", + "", + "// Go back to the delete citizen queue driver.", + "postman.setNextRequest(\"Delete citizen queue driver\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Temporary add MSP service request", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"current_client\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run create tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "var svcReq = jsonData.service_request;", + "", + "// Run service request tests.", + "eval(environment.one_service_request_test);", + "", + "// Make sure the response is valid.", + "pm.test(\"Response has service_request property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"service_request\")).to.be.true;", + "});", + "pm.test(\"Response has errors property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"errors\")).to.be.true;", + "});", + "", + "// Go back to the clear citizen driver.", + "postman.setNextRequest(\"Next citizen finish service\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : 1,\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "End clear queue via healthz endpoint", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check citizen through queue (QT1)", + "item": [ + { + "name": "Check no citizens (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "", + "// Get the SR ID.", + "if (jsonData.hasOwnProperty(\"service_request\")) {", + "\tcurrentSrId = jsonData.service_request.sr_id;", + "}", + "else {", + " currentSrId = 0;", + "}", + "", + "// The service request ID must not be 0.", + "pm.test(\"The Service Request ID is \" + currentSrId.toString() + \", must not be 0\", function() {", + " pm.expect(currentSrId).to.not.be.eql(0)", + "});", + "", + "// Save the service request ID.", + "postman.setEnvironmentVariable(\"first_sr_id\", currentSrId);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add MSP via email service request (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"current_client\",\"citizen_quantity\",\"channel_email_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Activate property tax service (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_sr_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get the current service request ID.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "", + "if (jsonData.hasOwnProperty(\"service_request\")) {", + "\tcurrentSrId = jsonData.service_request.sr_id;", + "}", + "else {", + " currentSrId = 0;", + "}", + "", + "// Get the first service request ID.", + "first_id = JSON.parse(postman.getEnvironmentVariable(\"first_sr_id\"));", + "", + "// The service request ID must not be 0.", + "pm.test(\"The Service Request ID is \" + currentSrId.toString() + \", must be \" + first_id.toString(), function() {", + " pm.expect(currentSrId).to.be.eql(first_id)", + "});", + "", + "// Save the service request ID.", + "postman.setEnvironmentVariable(\"second_sr_id\", currentSrId);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}service_requests/{{first_sr_id}}/activate/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{first_sr_id}}", + "activate", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be two service requests', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(2);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 5 (five periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(5);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen begin-hold-finish (QT2)", + "item": [ + { + "name": "Check no citizens (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "} ", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Place citizen on hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "place_on_hold", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Get service requests (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_request_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get periods for the first service request.", + " var allPeriods = allElements[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " var allPeriodCount = 0;", + "", + " // Find how many periods there are with null end time.", + " // Also, check schema.", + " allPeriods.forEach(function(onePeriod) {", + " ", + " // Find the open period.", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " ", + " });", + "} ", + "", + "// If there are some service requests, proceed with tests.", + "if (allElements !== null) {", + "", + " // Perform tests.", + " pm.test('There must be only one service request', function() {", + " pm.expect(allElements.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have three periods', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('Service request period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/service_requests/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "service_requests", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Call citizen from hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = allElements[0].service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen leave after create (QT3)", + "item": [ + { + "name": "Check no citizens (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = null;", + " if (currentCitizen.service_reqs.length !== 0) {", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " }", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " if (allPeriods !== null) {", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + " }", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen leave after waiting (QT4)", + "item": [ + { + "name": "Check no citizens (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check no citizens (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check update service information (QT5)", + "item": [ + { + "name": "Check no citizens (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// };", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "", + " // Save the service request ID for later.", + " var mySRId = allElements[0].service_reqs[0].sr_id;", + " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update quantity from 3 to 5 (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"citizen_quantity_update\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_request_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + "", + " // Perform tests.", + " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update service from PropTax to MSP (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"service_MSP_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_request_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + "", + " // Perform tests.", + " pm.test('Updated service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (should be two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check pick qtxn customer (QT6)", + "item": [ + { + "name": "Check no citizens (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(citizenCount).to.be.eql(0);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Create (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Edit (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Prop Tax via phone (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"first_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - List (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Add to queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Create (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/1/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "1", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Edit QTxn (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\",\"citizen_name_quick\",\"citizen_comment_quick\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name_quick\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment_quick\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments,", + "// \"qt_xn_citizen_ind\" : 1", + "// };", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + "", + " // Perform tests.", + " pm.test(\"Should be updating single citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - MSP via email (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"second_client\",\"citizen_quantity_update\",\"channel_email_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - List (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + " // Perform tests.", + " pm.test('Should be updating one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Add to queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should be adding one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check 2 citizens in queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test('Must be two active citizens in the office', function() {", + " pm.expect(citizenCount).to.be.eql(2);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Set CSR to QTxn (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"operator_token\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Turn the qtxn ID from character to number.", + "qtxn_id_number = JSON.parse(environment.qtxn_id);", + "postman.setEnvironmentVariable(\"qtxn_id_number\", qtxn_id_number);" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body, test the schema", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.onecsr_schema_check)", + "", + "// Ensure the CSR is now set to take quick transactions.", + "csrType = jsonData.csr.counter;", + "csrNeed = JSON.parse(environment.qtxn_id_number);", + "pm.test('CSR counter type is ' + csrType.toString() + '; must be ' + csrNeed.toString(), function() {", + " pm.expect(csrType).to.be.eql(csrNeed);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"counter_id\": {{qtxn_id_number}}\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be second one (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - begin serving (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - finish serving (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be first one (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - citizen left (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizen being served\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check pick non-qtxn customer (QT7)", + "item": [ + { + "name": "Bootstrap non-QTxn CSR (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse the current CSR payload.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.csr_schema_check);", + "", + "pm.environment.unset(\"first_client\");", + "pm.environment.unset(\"second_client\");", + "pm.environment.unset(\"current_client\");", + "", + "pm.test(\"Response should have csr property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", + "});", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + " postman.setEnvironmentVariable(\"current_office_id\", jsonData.csr.office_id);", + " postman.setEnvironmentVariable(\"current_office_number\", jsonData.csr.office.office_number);", + " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", + "}", + "", + "pm.test(\"Non-QTxn CSR id is available\", function(){", + " pm.expect(pm.environment.get(\"current_csr_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check no citizens (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(citizenCount).to.be.eql(0);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Create (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", + " pm.test(\"Stored first_client for later QT7 requests\", function() {", + " pm.expect(postman.getEnvironmentVariable(\"first_client\")).to.not.be.oneOf([null, undefined, \"\"]);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Edit QTxn (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"nonqtxn_token\",\"citizen_name_quick\",\"citizen_comment_quick\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + "", + " // Perform tests.", + " pm.test(\"Must be editing only one citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - MSP via email (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\",\"service_MSP_id\",\"first_client\",\"citizen_quantity_update\",\"channel_email_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - List (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + " // Perform tests.", + " pm.test('Must be editing only one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Add to queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be adding only one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Create (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", + " pm.test(\"Stored second_client for later QT7 requests\", function() {", + " pm.expect(postman.getEnvironmentVariable(\"second_client\")).to.not.be.oneOf([null, undefined, \"\"]);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/1/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "1", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Edit (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"nonqtxn_token\",\"citizen_name\",\"citizen_comment\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Should be updating single citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Prop Tax via phone (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\",\"service_PropTax_id\",\"second_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get the current service request, check schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.one_service_request_test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - List (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + " // Perform tests.", + " pm.test('Should be updating one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Add to queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should be adding one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check 2 citizens in queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test('Must be two active citizens in the office', function() {", + " pm.expect(citizenCount).to.be.eql(2);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Set CSR to Counter (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"nonqtxn_token\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Turn the qtxn ID from character to number.", + "counter_id_number = JSON.parse(environment.counter_id);", + "postman.setEnvironmentVariable(\"counter_id_number\", counter_id_number);" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body, test the schema", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.onecsr_schema_check)", + "", + "// Ensure the CSR is now set to take quick transactions.", + "csrType = jsonData.csr.counter;", + "csrNeed = JSON.parse(environment.counter_id_number);", + "pm.test('CSR counter type is ' + csrType.toString() + '; must be ' + csrNeed.toString(), function() {", + " pm.expect(csrType).to.be.eql(csrNeed);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"counter_id\": {{counter_id_number}}\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be second one (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "pm.test(\"Stored current_client for later QT7 requests\", function() {", + " pm.expect(postman.getEnvironmentVariable(\"current_client\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Content-Type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - begin serving (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - finish serving (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be first one (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "pm.test(\"Stored current_client for later QT7 requests\", function() {", + " pm.expect(postman.getEnvironmentVariable(\"current_client\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - citizen left (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"nonqtxn_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizen being served\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{nonqtxn_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Setup Booking", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "", + "// Hardcode recurring booking and appointment UUIDs.", + "pm.globals.set('pm_booking_uuid', JSON.stringify(pm.variables.replaceIn('{{$guid}}')));", + "pm.globals.set('pm_appt_uuid', JSON.stringify(pm.variables.replaceIn('{{$guid}}')));", + "", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "", + " pm.globals.set('public_url', pm.globals.get(usePrefix + 'public_url'));", + " pm.globals.set('public_user_id', pm.globals.get(usePrefix + 'public_user_id'));", + " pm.globals.set('public_user_password', pm.globals.get(usePrefix + 'public_user_password'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "console.log(\" --> booking_uuid: \" + pm.globals.get(\"pm_booking_uuid\"));", + "console.log(\" --> appointment_uuid: \" + pm.globals.get(\"pm_appt_uuid\"));", + "console.log(\" --> public_url: \" + pm.globals.get(\"public_url\"));", + "console.log(\" --> public_user_id: \" + pm.globals.get(\"public_user_id\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"qtxn_id_number\");", + "pm.environment.unset(\"counter_id_number\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Authentication Token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + } + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Type-Data", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"init_exam_type_data\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"IPSE - 4HR Single Exam\", weight: 40.2, id: find_exam(\"IPSE - 4HR Single Exam\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Get-Random", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_random_functions\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Room-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"room_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar roomSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"rooms\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"deleted\", \"office\", \"room_id\", \"room_name\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"rooms\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Room Schema\", function(){\n pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\",\n \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"notes\": {\"type\": \"string\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_email\": {\"type\": [\"null\", \"string\"]},\n \"exam_destroyed_date\": {\"type\": [\"null\", \"string\"]},\n \"receipt\": {\"type\": [\"null\", \"number\", \"string\"]},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\",\n \"office_number\", \"timezone\"]\n },\n \"exam_id\": {\"type\": \"number\"},\n \"bcmp_job_id\": {\"type\": [\"null\", \"number\"]},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"sbc_managed_ind\": {\"type\": \"number\"},\n \"invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"examinee_phone\": {\"type\": [\"null\", \"string\"]},\n \"upload_received_ind\": {\"type\": \"number\"},\n \"booking_id\": {\"type\": [\"null\", \"number\"]},\n \"booking\": {\"type\": [\"null\", \"object\"]},\n \"invigilator\": {\"type\": [\"null\", \"object\"]},\n \"is_pesticide\": {\"type\": \"number\"},\n \"exam_returned_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_email\": {\"type\": [\"null\", \"string\"]},\n \"candidates_list\": {\"type\": [\"null\", \"array\", \"object\"]},\n \"exam_returned_tracking_number\": {\"type\": [\"null\", \"string\"]},\n \"payee_name\": {\"type\": [\"null\", \"string\"]},\n \"deleted_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_phone\": {\"type\": [\"null\", \"string\"]},\n \"event_id\": {\"type\": \"string\"},\n \"payee_ind\": {\"type\": \"number\"},\n \"exam_received_date\": {\"type\": [\"null\", \"string\"]},\n \"number_of_students\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"offsite_location\": {\"type\": [\"null\", \"string\"]},\n \"exam_name\": {\"type\": \"string\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"receipt_sent_ind\": {\"type\": \"number\"}\n },\n \"required\": [\"exam_type\", \"office_id\", \"exam_type_id\", \"notes\", \"exam_written_ind\", \"examinee_email\", \"exam_destroyed_date\", \"receipt\",\n \"session_number\", \"office\", \"exam_id\", \"bcmp_job_id\", \"expiry_date\", \"sbc_managed_ind\", \"invigilator_id\", \"examinee_phone\",\n \"upload_received_ind\", \"booking_id\", \"booking\", \"invigilator\", \"is_pesticide\", \"exam_returned_date\", \"payee_email\",\n \"candidates_list\", \"exam_returned_tracking_number\", \"payee_name\", \"deleted_date\", \"payee_phone\", \"event_id\", \"payee_ind\",\n \"exam_received_date\", \"number_of_students\", \"exam_method\", \"offsite_location\", \"exam_name\", \"examinee_name\", \"receipt_sent_ind\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"exam\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\",\n \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"notes\": {\"type\": [\"null\", \"string\"]},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_email\": {\"type\": [\"null\", \"string\"]},\n \"exam_destroyed_date\": {\"type\": [\"null\", \"string\"]},\n \"receipt\": {\"type\": [\"null\", \"string\"]},\n \"session_number\": {\"type\": [\"number\", \"null\", \"string\"]},\n \"exam_id\": {\"type\": \"number\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"sbc_managed_ind\": {\"type\": \"number\"},\n \"invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"examinee_phone\": {\"type\": [\"null\", \"string\"]},\n \"upload_received_ind\": {\"type\": \"number\"},\n \"booking_id\": {\"type\": [\"null\", \"number\"]},\n \"booking\": {\"type\": [\"null\", \"object\"]},\n \"examinee_name\": {\"type\": [\"null\", \"string\"]},\n \"invigilator\": {\"type\": [\"null\", \"object\"]},\n \"is_pesticide\": {\"type\": \"number\"},\n \"exam_returned_date\": {\"type\": [\"null\", \"string\"]},\n \"payee_email\": {\"type\": [\"null\", \"string\"]},\n \"candidates_list\": {\"type\": [\"null\", \"array\", \"object\"]},\n \"exam_returned_tracking_number\": {\"type\": [\"null\", \"string\"]},\n \"payee_name\": {\"type\": [\"null\", \"string\"]},\n \"deleted_date\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"payee_phone\": {\"type\": [\"null\", \"string\"]},\n \"event_id\": {\"type\": [\"null\", \"string\"]},\n \"payee_ind\": {\"type\": \"number\"},\n \"exam_received_date\": {\"type\": [\"null\", \"string\"]},\n \"number_of_students\": {\"type\": [\"null\", \"number\"]},\n \"exam_method\": {\"type\": \"string\"},\n \"offsite_location\": {\"type\": [\"null\", \"string\"]},\n \"receipt_sent_ind\": {\"type\": \"number\"},\n \"exam_name\": {\"type\": \"string\"},\n \"bcmp_job_id\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"exam_type\", \"office_id\", \"exam_type_id\", \"notes\", \"exam_written_ind\", \"examinee_email\", \"exam_destroyed_date\",\n \"receipt\", \"session_number\", \"exam_id\", \"expiry_date\", \"sbc_managed_ind\", \"invigilator_id\",\n \"examinee_phone\", \"upload_received_ind\", \"booking_id\", \"booking\", \"examinee_name\", \"invigilator\",\n \"is_pesticide\", \"exam_returned_date\", \"payee_email\", \"candidates_list\", \"exam_returned_tracking_number\",\n \"payee_name\", \"deleted_date\", \"office\", \"payee_phone\", \"event_id\", \"payee_ind\", \"exam_received_date\",\n \"number_of_students\", \"exam_method\", \"offsite_location\", \"receipt_sent_ind\", \"exam_name\", \"bcmp_job_id\"]\n },\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"exams\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_name\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"room_id\": {\"type\": [\"null\", \"number\"]},\n \"blackout_notes\": {\"type\": [\"null\", \"string\"]},\n \"shadow_invigilator_id\": {\"type\": [\"null\", \"number\"]},\n \"room\": {\n \"type\": [\"null\", \"object\"],\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"deleted\", \"room_id\", \"room_name\"]\n },\n \"booking_id\": {\"type\": \"number\"},\n \"end_time\": {\"type\": \"string\"},\n \"booking_contact_information\": {\"type\": [\"null\", \"string\"]},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"invigilators\": {\"type\": \"array\"},\n \"fees\": {\"type\": [\"null\", \"string\"]},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_name\", \"blackout_flag\", \"sbc_staff_invigilated\", \"office_id\", \"room_id\", \"blackout_notes\",\n \"shadow_invigilator_id\", \"room\", \"booking_id\", \"end_time\", \"booking_contact_information\", \"recurring_uuid\",\n \"office\", \"invigilators\", \"fees\", \"start_time\"]\n },\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Invigilator-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"invigilator_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar invigilatorSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"invigilators\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"contact_email\": {\"type\": \"string\"},\n \"contact_phone\": {\"type\": \"string\"},\n \"contract_expiry_date\": {\"type\": \"string\"},\n \"contract_number\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"invigilator_id\": {\"type\": \"number\"},\n \"invigilator_name\": {\"type\": \"string\"},\n \"invigilator_notes\": {\"type\": [\"null\", \"string\"]},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"shadow_count\": {\"type\": \"number\"},\n \"shadow_flag\": {\"type\": \"string\"}\n },\n \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\",\n \"deleted\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\",\n \"office\", \"office_id\", \"shadow_count\", \"shadow_flag\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"invigilators\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Invigilator Response Schema\", function(){\n pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Invig-One-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"invig_one_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar invigilatorSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"invigilator\": {\n \"type\": \"object\",\n \"properties\": {\n \"contact_email\": {\"type\": \"string\"},\n \"contact_phone\": {\"type\": \"string\"},\n \"contract_expiry_date\": {\"type\": \"string\"},\n \"contract_number\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"invigilator_id\": {\"type\": \"number\"},\n \"invigilator_name\": {\"type\": \"string\"},\n \"invigilator_notes\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_duration\": {\"type\": [\"null\", \"number\"]},\n \"appointments_days_limit\": {\"type\": [\"null\", \"number\"]},\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"back_office_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"civic_address\": {\"type\": [\"null\", \"string\"]},\n \"counters\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"counter_id\": {\"type\": \"number\"},\n \"counter_name\": {\"type\": \"string\"}\n },\n \"required\": [\"counter_id\", \"counter_name\"]\n },\n },\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"latitude\": {\"type\": [\"null\", \"number\"]},\n \"longitude\": {\"type\": [\"null\", \"number\"]},\n \"max_person_appointment_per_day\": {\"type\": [\"null\", \"number\"]},\n \"office_appointment_message\": {\"type\": [\"null\", \"string\"]},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"online_status\": {\"type\": \"string\"},\n \"quick_list\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"actual_service_ind\": {\"type\": \"number\"},\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\",\n \"external_service_name\", \"online_availability\",\n \"online_link\", \"parent\", \"parent_id\", \"prefix\",\n \"service_code\", \"service_desc\", \"service_id\",\n \"service_name\"]\n },\n },\n \"sb\": {\n \"type\": \"object\",\n \"properties\": {\n \"sb_id\": {\"type\": \"number\"},\n \"sb_type\": {\"type\": \"string\"}\n },\n \"required\": [\"sb_id\", \"sb_type\"]\n },\n \"sb_id\": {\"type\": \"number\"},\n \"telephone\": {\"type\": [\"null\", \"string\"]},\n \"timeslots\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"day_of_week\": {\"type\": [\"null\", \"array\"]},\n \"end_time\": {\"type\": \"string\"},\n \"no_of_slots\": {\"type\": \"number\"},\n \"office\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"day_of_week\", \"end_time\", \"no_of_slots\",\n \"office\", \"start_time\"]\n },\n },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n }\n },\n \"required\": [\"appointment_duration\", \"appointments_days_limit\",\n \"appointments_enabled_ind\", \"back_office_list\", \"civic_address\",\n \"counters\", \"exams_enabled_ind\", \"latitude\",\n \"longitude\", \"max_person_appointment_per_day\",\n \"office_appointment_message\", \"office_id\", \"office_name\",\n \"office_number\", \"online_status\", \"quick_list\", \"sb\", \"sb_id\",\n \"telephone\", \"timeslots\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"shadow_count\": {\"type\": \"number\"},\n \"shadow_flag\": {\"type\": \"string\"}\n },\n \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\",\n \"deleted\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\",\n \"office\", \"office_id\", \"shadow_count\", \"shadow_flag\"]\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"invigilator\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Invigilator Response Schema\", function(){\n pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointment\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"online_flag\": {\"type\": \"boolean\"},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"services\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n }\n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\",\n \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\",\n \"start_time\", \"blackout_flag\", \"citizen_id\", \"online_flag\",\n \"recurring_uuid\", \"service\"],\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"appointment\", \"errors\"],\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointments Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an appointment property.\npm.test(\"Response should have appointment property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"appointment\")).to.be.true;\n});\n\nvar name = jsonData.appointment.citizen_name;\nvar comments = jsonData.appointment.comments;\nvar contact = jsonData.appointment.contact_information;\nvar name_expect = JSON.parse(pm.globals.get(\"appt_citizen_name\"));\nvar comments_expect = JSON.parse(pm.globals.get(\"appt_comments\"));\nvar contact_expect = JSON.parse(pm.globals.get(\"appt_contact_information\"));\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"appointment\")) {\n\n //Test to see if service id has expected value\n pm.test(\"Validate Service ID has expected value\", function(){\n pm.expect(jsonData.service_id === environment.service_id);\n });\n\n //Test to see if office id has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if category has expected value\n pm.test(\"Validate category has expected value\", function(){\n pm.expect(jsonData.category === environment.category);\n });\n\n //Test to see if citizen name field has expected value\n pm.test(\"Name should be '\" + name_expect + \"', it is '\" + name + \"'\", function(){\n pm.expect(name).to.be.eql(name_expect);\n });\n //Test to see if comments field has expected value\n pm.test(\"Comments should be '\" + comments_expect + \"'; it is '\" + comments +\"'\", function(){\n pm.expect(comments).to.be.eql(comments_expect);\n });\n //Test to see if contact info field has expected value\n pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact +\"'\", function(){\n pm.expect(contact).to.be.eql(contact_expect);\n });\n};\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_list_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointments\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": [\"null\", \"string\"]},\n \"contact_information\": {\"type\": [\"null\", \"string\"]},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"online_flag\": {\"type\": \"boolean\"},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"services\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n },\n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\",\n \"contact_information\", \"end_time\", \"office\", \"office_id\",\n \"service_id\", \"start_time\", \"blackout_flag\", \"citizen_id\",\n \"online_flag\", \"recurring_uuid\", \"service\"]\n }\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"appointments\"],\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointment List Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Make sure the CSR schema is OK.", + "eval(environment.csr_schema_check)", + "", + "//var jsonData = pm.response.json();", + "//pm.environment.set(\"csr_schema_check\", jsonData.data);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Room IDs", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.room_schema_check);", + "", + "if (jsonData.hasOwnProperty(\"rooms\")) {", + " room_id_1 = jsonData.rooms[0].room_id;", + " room_id_2 = room_id_1;", + " if (jsonData.rooms.length > 1) {", + " room_id_2 = jsonData.rooms[1].room_id;", + " }", + " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", + " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Service IDs", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data, check the schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.service_schema_check);", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", + " ", + " // Test the authenticate response.", + " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", + " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", + " });", + " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", + " pm.expect(element.actual_service_ind).to.be.eql(1);", + " });", + " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - Rural PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the service IDs.", + "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver Booking", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Set health response time variable.", + "max_response_time = 1500;", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"max_response_time\", JSON.stringify(max_response_time));", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is health'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_response_time) {", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint Booking\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint Booking", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is health'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint Booking\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint Booking", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints" + }, + { + "name": "Get Exam Types", + "item": [ + { + "name": "Exam Type List", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Define the JSON Schema expected in response", + "var examTypeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_types\": {", + " \"type\": \"array\",", + " \"items\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_color\": {\"type\": \"string\"},", + " \"exam_type_id\": {\"type\": \"number\"},", + " \"exam_type_name\": {\"type\": \"string\"},", + " \"group_exam_ind\": {\"type\": \"number\"},", + " \"ita_ind\": {\"type\": \"number\"},", + " \"method_type\": {\"type\": \"string\"},", + " \"number_of_hours\": {\"type\": \"number\"},", + " \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},", + " \"pesticide_exam_ind\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\",", + " \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]", + " }", + " },", + " \"errors\": {\"type\": [\"object\", \"string\"]}", + " },", + " \"required\": [\"exam_types\", \"errors\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Exam type response has valid schema\", function(){", + " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", + "});", + "", + "// Store all exam type IDs for future use in adding exams", + "var allExamIds = [];", + "", + "// Make sure some data returned.", + "pm.test(\"Response has exam_types property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", + "});", + "pm.test(\"Response has at least one exam_type\", function(){", + " pm.expect(jsonData.exam_types.length).to.be.above(0);", + "});", + "", + "// Set up list of valid exam types, create random functions.", + "eval(environment.init_exam_type_data);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exam_types/", + "host": [ + "{{url}}exam_types" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Exams", + "item": [ + { + "name": "Exam Post End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", + "// Store other variables for later use.", + "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", + "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", + "// Calculate an expiry date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format expiry date for the exam.", + "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "", + "// If jsonData has an exam property, save exam ID.", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_list_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Put End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"exam_method\",\"random_exam_type_id\",\"exam_written_ind\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = eventNumber;", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId.toString()));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam by ID", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam by Event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "// Define the JSON Schema expected in response", + "var examSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"message\": {\"type\": \"boolean\"}", + " },", + " \"required\": [\"message\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Exam Schema\", function(){", + " pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;", + "});", + "", + "// Make sure the message is true.", + "pm.test(\"Result is '\" + jsonData.message.toString() + \"', it should be 'true'\", function() {", + " pm.expect(jsonData.message).to.be.eql(true);", + "});", + "", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"event_number\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Put the event ID in the right format.\r", + "var event_number = JSON.parse(postman.getEnvironmentVariable(\"update_id\"))\r" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/event_id/{{event_number}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "event_id", + "{{event_number}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Exams Export", + "item": [ + { + "name": "Exams Export List", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than 20,000ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(20000);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date four weeks prior to today.", + "var start = new Date();", + "start.setDate(start.getDate()-28);", + "// Get year, day, month from the start time.", + "start_year = start.getFullYear().toString();", + "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", + "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", + "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", + "// Get year, day, month from the current day.", + "var today = new Date();", + "end_year = today.getFullYear().toString();", + "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", + "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", + "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", + "console.log(\"Start: \" + start_date);", + "console.log(\"End: \" + end_date);", + "pm.globals.set(\"start_date\", start_date);", + "pm.globals.set(\"end_date\", end_date);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "export", + "" + ], + "query": [ + { + "key": "start_date", + "value": "{{start_date}}" + }, + { + "key": "end_date", + "value": "{{end_date}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Bookings", + "item": [ + { + "name": "Booking Post End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Put End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point Again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Booking Recurring", + "item": [ + { + "name": "Book first recurring event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\",\"pm_booking_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", + "pm.globals.set(\"booking_contact_information\", JSON.stringify(\"Postman Contact\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Book second recurring event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"second_event_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"room_id\",\"fees\",\"booking_name\",\"booking_contact_information\",\"current_office_id\",\"pm_booking_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+8);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Book third recurring event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"third_event_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"room_id\",\"fees\",\"booking_name\",\"booking_contact_information\",\"current_office_id\",\"pm_booking_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+9);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"booking_contact_information\": {{booking_contact_information}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_booking_uuid}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check first booking", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", + "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second booking", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", + "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{second_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{second_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third booking", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", + "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{third_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{third_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update second event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\n \"booking_name\": \"Second booking event updated\",\n \"booking_contact_information\": \"My_favourite_postman@gmail.com\"\n}" + }, + "url": { + "raw": "{{url}}bookings/{{second_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{second_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check first booking again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", + "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second booking after update", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = \"Second booking event updated\";", + "var contact_expect = \"My_favourite_postman@gmail.com\";", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{second_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{second_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third booking again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = JSON.parse(pm.globals.get(\"booking_name\"));", + "var contact_expect = JSON.parse(pm.globals.get(\"booking_contact_information\"));", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{third_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{third_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update all events", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "// Define the JSON Schema expected in response", + "var examSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"bookings\": {\"type\": \"object\"},", + " \"errors\": {\"type\": [\"object\", \"string\"]}", + " },", + " \"required\": [\"bookings\", \"errors\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Update all events schema\", function(){", + " pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Print out the recurring booking ID.", + "console.log(\"Recurring booking uuid:\");", + "console.log(pm.globals.get(\"pm_booking_uuid\"));", + "pm_url_uuid = JSON.parse(pm.globals.get(\"pm_booking_uuid\"))", + "console.log(pm_url_uuid);", + "pm.globals.set(\"pm_url_uuid\", pm_url_uuid);", + " ", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\n \"booking_name\": \"All recurring pm events #2\",\n \"booking_contact_information\": \"My_second_favourite_postman@gmail.com\",\n \"invigilator_id\": []\n}" + }, + "url": { + "raw": "{{url}}bookings/recurring/{{pm_url_uuid}}", + "host": [ + "{{url}}bookings" + ], + "path": [ + "recurring", + "{{pm_url_uuid}}" + ] + } + }, + "response": [] + }, + { + "name": "Check first booking update all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = \"All recurring pm events #2\";", + "var contact_expect = \"My_second_favourite_postman@gmail.com\";", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second booking update all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = \"All recurring pm events #2\";", + "var contact_expect = \"My_second_favourite_postman@gmail.com\";", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{second_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{second_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third booking update all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_event_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "// Get booking data.", + "var name = jsonData.booking.booking_name;", + "var contact = jsonData.booking.booking_contact_information;", + "var name_expect = \"All recurring pm events #2\";", + "var contact_expect = \"My_second_favourite_postman@gmail.com\";", + "", + "// Make sure data for the first booking hasn't changed.", + "pm.test(\"Name should be '\" + name_expect + \"'; it is '\" + name + \"'\", function () {", + " pm.expect(name).to.be.eql(name_expect);", + "});", + "pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact + \"'\", function () {", + " pm.expect(contact).to.be.eql(contact_expect);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{third_event_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{third_event_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"pm_url_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/recurring/{{pm_url_uuid}}", + "host": [ + "{{url}}bookings" + ], + "path": [ + "recurring", + "{{pm_url_uuid}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Invigilators checks", + "item": [ + { + "name": "Invigilators", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.invigilator_schema_check);", + "", + "// Make sure there is at least one invigilator.", + "invigilators = jsonData.invigilators;", + "pm.test(\"Number of invigilators is \" + invigilators.length.toString() + \", must not be 0\", function() {", + " pm.expect(invigilators.length).to.not.be.eql(0);", + "})", + "", + "// Get the first invigilator.", + "first = invigilators[0];", + "", + "// Make sure the invigilator shadow count is 2, and the flag is \"Y\"", + "pm.test(\"Invigilator shadow count is \" + first.shadow_count.toString() + \", must be 2\", function() {", + " pm.expect(first.shadow_count).to.be.eql(2);", + "})", + "pm.test(\"Invigilator shadow flag is \" + first.shadow_flag.toString() + \", must be 'Y'\", function() {", + " pm.expect(first.shadow_flag).to.be.eql(\"Y\");", + "})", + "", + "// Save some values of the first invigilator.", + "invId = first.invigilator_id;", + "count = first.shadow_count;", + "flag = first.shadow_flag;", + "postman.setEnvironmentVariable(\"inv_id\", invId);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}invigilators/", + "host": [ + "{{url}}invigilators" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Invigilators offsite", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body, test the schema.", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.invigilator_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}invigilators/offsite/", + "host": [ + "{{url}}invigilators" + ], + "path": [ + "offsite", + "" + ] + } + }, + "response": [] + }, + { + "name": "Invigilator subtract", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"inv_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.invig_one_schema_check);", + "", + "first = jsonData.invigilator;", + "", + "// Save some values.", + "newId = first.invigilator_id;", + "newEmail = first.contact_email;", + "newPhone = first.contact_phone;", + "newNotes = first.invigilator_notes;", + "newCount = first.shadow_count;", + "newFlag = first.shadow_flag;", + "", + "// Temporary debugging.", + "console.log(\"==> New invigilator data:\");", + "console.log(\" --> id: \" + newId);", + "console.log(\" --> email: \" + newEmail);", + "console.log(\" --> phone: \" + newPhone);", + "console.log(\" --> notes: \" + newNotes);", + "console.log(\" --> count: \" + newCount);", + "console.log(\" --> flag: \" + newFlag);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "", + "value": "", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}invigilator/{{inv_id}}/?subtract=True&add=False", + "host": [ + "{{url}}invigilator" + ], + "path": [ + "{{inv_id}}", + "" + ], + "query": [ + { + "key": "subtract", + "value": "True" + }, + { + "key": "add", + "value": "False" + } + ] + } + }, + "response": [] + }, + { + "name": "Invigilator add", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"inv_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.invig_one_schema_check);", + "", + "first = jsonData.invigilator;", + "", + "// Save some values.", + "newId = first.invigilator_id;", + "newEmail = first.contact_email;", + "newPhone = first.contact_phone;", + "newNotes = first.invigilator_notes;", + "newCount = first.shadow_count;", + "newFlag = first.shadow_flag;", + "", + "// Temporary debugging.", + "console.log(\"==> New invigilator data:\");", + "console.log(\" --> id: \" + newId);", + "console.log(\" --> email: \" + newEmail);", + "console.log(\" --> phone: \" + newPhone);", + "console.log(\" --> notes: \" + newNotes);", + "console.log(\" --> count: \" + newCount);", + "console.log(\" --> flag: \" + newFlag);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "", + "value": "", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}invigilator/{{inv_id}}/?subtract=False&add=True", + "host": [ + "{{url}}invigilator" + ], + "path": [ + "{{inv_id}}", + "" + ], + "query": [ + { + "key": "subtract", + "value": "False" + }, + { + "key": "add", + "value": "True" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Rooms", + "item": [ + { + "name": "Room List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.room_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "CSRS", + "item": [ + { + "name": "CSRS Me Get End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.csr_schema_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Offices", + "item": [ + { + "name": "Office List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "eval(environment.all_office_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}offices/", + "host": [ + "{{url}}offices" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Office Available Timeslots", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}offices/{{current_office_id}}/slots/", + "host": [ + "{{url}}offices" + ], + "path": [ + "{{current_office_id}}", + "slots", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Appointments", + "item": [ + { + "name": "Appointment Post End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"My contact info\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid\\", + "eval(environment.appointment_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Put End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\",\"current_office_id\",\"category\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"super EARLY\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}}\n}" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}?office_id={{current_office_id}}", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ], + "query": [ + { + "key": "office_id", + "value": "{{current_office_id}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Appointments Recurring", + "item": [ + { + "name": "Book first appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));", + "pm.test(\"Stored appointment_id for recurring checks\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"appointment_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\",\"pm_appt_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"second_appt_id\");", + "pm.environment.unset(\"third_appt_id\");", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT PERIOD\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"Office needs a break\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Contact info, me@me.com\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Book second appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"second_appt_id\", JSON.stringify(appointment_id));", + "pm.test(\"Stored second_appt_id for recurring checks\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"second_appt_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appt_citizen_name\",\"appt_comments\",\"appt_contact_information\",\"current_office_id\",\"pm_appt_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "pm.environment.unset(\"second_appt_id\");", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+8);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Book third appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"third_appt_id\", JSON.stringify(appointment_id));", + "pm.test(\"Stored third_appt_id for recurring checks\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"third_appt_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appt_citizen_name\",\"appt_comments\",\"appt_contact_information\",\"current_office_id\",\"pm_appt_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "pm.environment.unset(\"third_appt_id\");", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+9);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"blackout_flag\" : \"Y\",\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"office_id\" : {{current_office_id}},\n \"recurring_uuid\": {{pm_appt_uuid}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check first appointment", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second appointment", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{second_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{second_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third appointment", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{third_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{third_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update second appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT UPDATE 2nd\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"Update blackout comments\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Update blackout contact\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{url}}appointments/{{second_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{second_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second appointment again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{second_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{second_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check first appointment again", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Reset variables back to their first and third appt values.\r", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT PERIOD\"));\r", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"Office needs a break\"));\r", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"Contact info, me@me.com\"));\r" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third appointment again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{third_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{third_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update all appointments", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "// Define the JSON Schema expected in response", + "var apptAllSchema = {", + " \"type\": \"object\", ", + " \"properties\": {", + " \"appointments\": {\"type\": \"object\"},", + " \"errors\": {\"type\": [\"object\", \"string\"]}", + " },", + " \"required\": [\"appointments\", \"errors\"],", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Recurring Appointments Schema\", function(){", + " pm.expect(tv4.validate(jsonData, apptAllSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Print out the appointment ID.", + "pm_url_uuid = JSON.parse(pm.globals.get(\"pm_appt_uuid\"))", + "pm.globals.set(\"pm_url_uuid\", pm_url_uuid);", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"BLACKOUT ALL UPDATE\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"All blackout comments\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"All Update blackout contacts\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"citizen_name\" : {{appt_citizen_name}},\n \"comments\" : {{appt_comments}},\n \"contact_information\" : {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{url}}appointments/recurring/{{pm_url_uuid}}", + "host": [ + "{{url}}appointments" + ], + "path": [ + "recurring", + "{{pm_url_uuid}}" + ] + } + }, + "response": [] + }, + { + "name": "Check first appointment all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check second appointment all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{second_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{second_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Check third appointment all", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"third_appt_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{third_appt_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{third_appt_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"pm_url_uuid\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/recurring/{{pm_url_uuid}}", + "host": [ + "{{url}}appointments" + ], + "path": [ + "recurring", + "{{pm_url_uuid}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Public User Appointments", + "item": [ + { + "name": "Authenticate public user token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"public_user_id\",\"public_user_password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"public_user_token\", jsonData.access_token);", + "pm.globals.set(\"public_user_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{public_user_id}}&password={{public_user_password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ] + } + }, + "response": [] + }, + { + "name": "Authenticate and create user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"clientid\",\"public_user_id\",\"public_user_password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "console.log(firstUser)", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "", + "//Dynamic variables used for end-point testing later on", + "var user_id = firstUser.user_id;", + "var user_phone = firstUser.telephone;", + "postman.setEnvironmentVariable(\"user_id\", JSON.stringify(user_id));", + "postman.setEnvironmentVariable(\"user_phone\", JSON.stringify(user_phone));", + "pm.test(\"Stored user_id for later public user requests\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"user_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Typea", + "type": "text", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{public_user_id}}&password={{public_user_password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{public_url}}users/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Discover public appointment office", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var offices = jsonData.offices || [];", + "var office = offices.find(function(candidate) {", + " return candidate.office_name === \"100 Mile House\";", + "});", + "", + "pm.test(\"Public appointment office was discovered\", function(){", + " pm.expect(office).to.not.be.oneOf([null, undefined]);", + "});", + "", + "pm.environment.set(\"public_office_id\", String(office.office_id));", + "pm.environment.set(\"public_office_timezone\", office.timezone.timezone_name);" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}offices/", + "host": [ + "{{public_url}}offices" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Discover public appointment service", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var services = jsonData.services || [];", + "var service = services.find(function(candidate) {", + " return candidate.service_name === \"Deferment Application\";", + "});", + "", + "pm.test(\"Public appointment service was discovered\", function(){", + " pm.expect(service).to.not.be.oneOf([null, undefined]);", + "});", + "", + "pm.environment.set(\"public_service_id\", String(service.service_id));" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}services/", + "host": [ + "{{public_url}}services" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Find available public appointment slot", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"public_office_id\",\"public_service_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var slotDay = null;", + "var slot = null;", + "", + "Object.keys(jsonData).forEach(function(dayKey) {", + " if (!slotDay && Array.isArray(jsonData[dayKey]) && jsonData[dayKey].length > 0) {", + " slotDay = dayKey;", + " slot = jsonData[dayKey][0];", + " }", + "});", + "", + "pm.test(\"At least one public appointment slot is available\", function(){", + " pm.expect(slotDay).to.not.be.oneOf([null, undefined, \"\"]);", + " pm.expect(slot).to.not.be.oneOf([null, undefined]);", + "});", + "", + "var timezoneName = pm.environment.get(\"public_office_timezone\") || \"America/Creston\";", + "var offset = timezoneName === \"America/Creston\" ? \"-07:00\" : \"-07:00\";", + "var parts = slotDay.split(\"/\");", + "var dayIso = [parts[2], parts[0], parts[1]].join(\"-\");", + "var startIso = dayIso + \"T\" + slot.start_time + \":00\" + offset;", + "var endIso = dayIso + \"T\" + slot.end_time + \":00\" + offset;", + "", + "pm.globals.set(\"public_start_time\", JSON.stringify(startIso));", + "pm.globals.set(\"public_end_time\", JSON.stringify(endIso));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"public_service_id\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"My self serve appt.\"));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"cfms-postman-public-user\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"test@test.com\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}offices/{{public_office_id}}/slots/?service_id={{public_service_id}}", + "host": [ + "{{public_url}}offices" + ], + "path": [ + "{{public_office_id}}", + "slots", + "" + ], + "query": [ + { + "key": "service_id", + "value": "{{public_service_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Book an appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "", + "// Update the comments to include phone number, before data test.", + "var comments_old = JSON.parse(pm.globals.get(\"appt_comments\"));", + "var user_phone = JSON.parse(postman.getEnvironmentVariable(\"user_phone\"));", + "var comments_new = comments_old + '. Phone: ' + user_phone;", + "console.log(\"==> Old: \" + comments_old);", + "console.log(\"==> Phone: \" + user_phone);", + "console.log(\"==> New: \" + comments_new);", + "pm.globals.set(\"appt_comments\", JSON.stringify(comments_new));", + "", + "// Now ready for the data check.", + "eval(environment.appointment_data_check);", + "", + "//Dynamic variable used for end-point testing later on", + "var appointment_id = jsonData.appointment.appointment_id;", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));", + "pm.test(\"Stored appointment_id for public appointment cleanup\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"appointment_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"service_id\",\"public_office_id\",\"public_start_time\",\"public_end_time\",\"category\",\"appt_comments\",\"appt_citizen_name\",\"appt_contact_information\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "var requiredVars = [\"public_start_time\", \"public_end_time\", \"service_id\"];", + "requiredVars.forEach(function(varName) {", + " pm.test(\"Required variable \" + varName + \" is set before public booking\", function(){", + " pm.expect(pm.globals.get(varName)).to.not.be.oneOf([null, undefined, \"\"]);", + " });", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{public_office_id}},\n \"start_time\": {{public_start_time}},\n \"end_time\": {{public_end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{public_url}}appointments/", + "host": [ + "{{public_url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Edit user profile", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"user_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"test@test.com\",\n \"telephone\": \"0000000000\",\n \"send_email_reminders\": true,\n \"send_sms_reminders\": true\n}" + }, + "url": { + "raw": "{{public_url}}users/{{user_id}}/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "{{user_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get user profile", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "", + "type": "text", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{public_url}}users/me/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "List all appointments", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid\\", + "eval(environment.appointment_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}users/appointments/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "appointments", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{public_url}}appointments/{{appointment_id}}/", + "host": [ + "{{public_url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ] +} diff --git a/api/postman/API_Test_TheQ_Booking_with_QTxn.json b/api/postman/API_Test_TheQ_Booking_with_QTxn.json index 94b13abf1..f73512536 100644 --- a/api/postman/API_Test_TheQ_Booking_with_QTxn.json +++ b/api/postman/API_Test_TheQ_Booking_with_QTxn.json @@ -1,13270 +1,14992 @@ { - "info": { - "_postman_id": "1c30decd-7b40-4a76-aa2b-e092dc3eb9dc", - "name": "API_Test_TheQ_Booking", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Setup TheQ", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.globals.set(\"token\", jsonData.access_token);\n\t pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n\t if (err) {\n\t console.log(err);\n\t }\n\t // console.log(err ? err : res.json());\n\t} else {\n\t pm.globals.set(\"token\", 0);\n\t pm.globals.set(\"refresh_token\", 0);\n\t pm.globals.set(\"token_expires\", 0);\n\t pm.globals.set(\"refresh_token_expires\", 0);\n\t}\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-Script", - "event": [ - { - "listen": "test", - "script": { - "id": "f386cf22-bb0f-47d9-9c22-cda162ed4375", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-AuthToken-Script", - "event": [ - { - "listen": "test", - "script": { - "id": "e1d0ab5c-fe6b-4e6d-8778-417ae879cee1", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_token_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: authurl + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\n\npm.sendRequest(echoPostRequest, function (err, res) {\n if (err) { console.log(err); }\n else {\n var jsonData = res.json();\n pm.globals.set(\"token\", jsonData.access_token);\n pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n pm.globals.set(\"token_expires\", Date.now()+(jsonData.expires_in * 1000));\n pm.globals.set(\"refresh_token_expires\", Date.now()+(jsonData.refresh_expires_in * 1000));\n }\n //console.log(err ? err : res.json());\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-AuthRefresh-Script", - "event": [ - { - "listen": "test", - "script": { - "id": "365727f4-696d-4e44-a379-5eba3001641a", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_refresh_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nrefresh_token = environment.refresh_token;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: authurl +'/auth/realms/' +realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=refresh_token&client_id=' + clientid \n + '&refresh_token=' + refresh_token \n + '&client_secret=' + client_secret\n }\n};\n\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n pm.globals.set(\"token\", jsonData.access_token);\n pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n pm.globals.set(\"token_expires\", Date.now()+(jsonData.expires_in * 1000));\n pm.globals.set(\"refresh_token_expires\", Date.now()+(jsonData.refresh_expires_in * 1000));\n\n console.log(err ? err : res.json());\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\r\nmax_response_time = JSON.parse(globals.max_response_time);\r\n\r\n// Check to make sure the response time was within the maximum allowed.\r\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\r\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\r\n});\r\n\r\n// Other tests.\r\npm.test(\"Response code for request is 200\", function(){\r\n pm.response.to.have.status(200);\r\n});\r\npm.test('Response header should have Content-Type of application/json', function() {\r\n pm.response.to.have.header('content-type', 'application/json');\r\n});\r\npm.test('Response body be in JSON format', function() {\r\n pm.response.to.be.json; \r\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Complex-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "f006b951-3205-4f21-a315-369f85591929", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"complex_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Create-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.response.to.have.status(201);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Citizen-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"citizen_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var schema = {\n \"properties\" : {\n \"start_time\" : {\n \"type\" : \"string\"\n },\n \"citizen_name\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"number\", \"object\"]\n },\n \"qt_xn_citizen_ind\" : {\n \"type\" : \"number\"\n },\n \"ticket_number\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"service_reqs\" : {\n \"type\" : \"array\"\n },\n \"office_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"cs\" : {\n \"type\" : \"object\"\n },\n \"citizen_comments\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \n },\n \"required\" : [\"start_time\", \"citizen_name\", \"citizen_id\",\n \"qt_xn_citizen_ind\", \"ticket_number\", \"service_reqs\",\n \"office_id\", \"cs\", \"citizen_comments\"]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n \n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Service-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "0e6c4264-28b3-4a49-b6bb-1199aef2cb13", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"service_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var schema = {\n \"properties\" : {\n \"sr_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"sr_state\" : {\n \"type\" : \"object\"\n },\n \"periods\" : {\n \"type\" : \"array\"\n },\n \"service\" : {\n \"type\" : \"object\"\n },\n \"citizen\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"quantity\" : {\n \"type\" : \"number\"\n },\n \"service_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"channel\" : {\n \t\"type\" : \"object\"\n },\n \"channel_id\" : {\n \t\"type\" : [\"object\", \"number\"]\n }\n \n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Get-Active-Citizens-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "8856712d-73ca-4172-89ba-590e8b015c6b", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - } - ], - "description": "This folder performs basic authentication features." - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Set health response time variable.", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum load time allowed.", - "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints" - }, - { - "name": "Check user login", - "item": [ - { - "name": "Authenticate default QTxn user", - "event": [ - { - "listen": "test", - "script": { - "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", - "exec": [ - "// Do the basic checks.", - "eval(environment.basic_response_test);", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " pm.expect(access_token).not.eql(null);", - "});", - "", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " pm.expect(refresh_token).not.eql(null);", - "});", - "", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " pm.expect(expires_in).not.eql(null);", - "});", - "", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " pm.expect(refresh_expires_in).not.eql(null);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "description": "Make sure the operator ID can log in" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check channels", - "item": [ - { - "name": "Get channels", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "2b71673c-4aa2-47b3-8594-15f02b390ee0", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "42742c4c-505d-472d-857b-fdba74cc49ac", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"channel_name\" : {", - " \"type\" : \"string\"", - " },", - " \"channel_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " }", - " },", - " \"required\" : [\"channel_name\", \"channel_id\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allChannels = jsonData.channels;", - "var channelCount = 0;", - "var phoneId = 0;", - "var emailId = 0;", - "var phoneText = \"Phone\";", - "var emailText = \"Email/Fax/Mail\";", - "allChannels.forEach(function(channel) {", - " channelCount ++;", - " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", - " tests[testTitle] = tv4.validate(channel, schema);", - " if (channel.channel_name === phoneText) {", - " phoneId = channel.channel_id;", - " }", - " if (channel.channel_name === emailText) {", - " emailId = channel.channel_id;", - " }", - "});", - "", - "// Check that you found the phone ID.", - "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", - " pm.expect(phoneId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the email ID.", - "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", - " pm.expect(emailId).to.not.be.eql(0);", - "});", - "", - "// Store this ID for future use.", - "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", - "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}channels/", - "host": [ - "{{url}}channels" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check counters", - "item": [ - { - "name": "Store CSR and Office Info", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", - " },", - " \"office_name\": {\"type\": \"string\"},", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " \"attention_needed\": {\"type\": \"boolean\"},", - " \"required\": [\"csr\", \"attention_needed\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});", - "", - "// Make sure that jsonData has an csr property.", - "pm.test(\"Response should have csr property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", - "});", - "", - "var csr = 0;", - "var office = 0;", - "var counters = 0;", - "var counter_text = \"Counter\";", - "var counter_id = 0;", - "var qtxn_text = \"Quick Trans\";", - "var qtxn_id = 0;", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", - " ", - " csr = jsonData.csr;", - " counter_id = 0;", - " qtxn_id = 0;", - "", - " // Make sure that jsonData has an booking property.", - " pm.test(\"CSR should have office property\", function(){", - " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", - " });", - " ", - " // Make sure office has counter property.", - " if (csr.hasOwnProperty(\"office\")) {", - " office = csr.office;", - " ", - " // Make sure that jsonData has an booking property.", - " pm.test(\"Office should have counters property\", function(){", - " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", - " });", - "", - " // Make sure office has counter property.", - " if (office.hasOwnProperty(\"counters\")) {", - " counters = office.counters;", - " ", - " // Search for Counter and Quick Trans counters", - " counters.forEach(function(counter) {", - " if (counter.counter_name === counter_text) {", - " counter_id = counter.counter_id;", - " }", - " if (counter.counter_name === qtxn_text) {", - " qtxn_id = counter.counter_id;", - " }", - " });", - " ", - " // Make sure you found the right IDs.", - " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", - " pm.expect(counter_id).to.not.be.eql(0);", - " });", - " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", - " pm.expect(qtxn_id).to.not.be.eql(0);", - " });", - " ", - " // Store the ids for future use.", - " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", - " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", - " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", - " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", - " }", - "", - " ", - " }", - "", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check categories", - "item": [ - { - "name": "Get categories", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "e99f23ee-5a03-43d9-9075-75dbf06aadcc", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1a9c9ff5-8475-4dd2-a6c7-ffb5d4689145", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allCategories = jsonData.categories;", - "var categoryCount = 0;", - "allCategories.forEach(function(category) {", - " categoryCount ++;", - " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", - " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", - " var displayInd = category.display_dashboard_ind;", - " var serviceInd = category.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.eql(0);", - " });", - "", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(0);", - " });", - " ", - " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", - " pm.expect(category.parent_id).to.be.null;", - " });", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}categories/", - "host": [ - "{{url}}categories" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check services", - "item": [ - { - "name": "Get services", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3b11031e-4031-4569-91f2-49e23517ffee", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"number\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", - " tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);", - " displayInd = element.display_dashboard_ind;", - " serviceInd = element.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.within(0, 1);", - " });", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(1);", - " });", - " ", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the MSP service.", - "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the Property Tax service.", - "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Clear queue for tests", - "item": [ - { - "name": "Delete citizen queue driver", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "7f6d8d4e-e4fe-451f-a5d9-119fcefcd527", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "27f63d12-7c7c-42e1-9647-85480ee17cb8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Delete citizens, if there are any.", - " if (citizenIds.length > 0) {", - " ", - " // Set the current_client, to be deleted.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(citizenIds));", - " ", - " if (currentCitizen.service_reqs.length === 0) {", - " postman.setNextRequest(\"Next citizen left\");", - " // // Temporary kludge. Citizen left not working, so add SR, then delete.", - " // postman.setNextRequest(\"Temporary add MSP service request\");", - " }", - " else {", - " postman.setNextRequest(\"Next citizen finish service\");", - " }", - " }", - " ", - " // No more citizens. Clear the current, queue variables.", - " else {", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(\"\"));", - " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(\"\"));", - " postman.setNextRequest(\"End clear queue via healthz endpoint\");", - " }", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Next citizen finish service", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e516a2b7-0555-43b5-8058-ccd92b5b6e95", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "var citizenToBeDeleted = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "var citizenData = jsonData.citizen;", - "var testTitle = \"Check citizen finish service\";", - "", - "// Make sure the response is valid.", - "pm.test(testTitle + \": Response should have property 'citizen'\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"citizen\")).to.be.true;", - "});", - "pm.test(testTitle + \": Response should not have property 'message' indicating an error\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"message\")).to.be.false;", - "});", - "pm.test(testTitle + \": Citizen marked as finished should be citizen \" + citizenToBeDeleted.toString(), function(){", - " pm.expect(citizenData.citizen_id).to.be.eql(citizenToBeDeleted);", - "});", - "", - "// Go back to the delete citizen queue driver.", - "postman.setNextRequest(\"Delete citizen queue driver\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Next citizen citizen left", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "6caff547-493f-4f0d-a121-f8e3398f11c3", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform checks.", - " pm.test(\"Check there are no citizens waiting\", function(){", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test(\"Citizen that left must be \" + currentCitizen.citizen_id.toString() + \" (is \" + currentCitizenId.toString(), function(){", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - "}", - "", - "// Go back to the delete citizen queue driver.", - "postman.setNextRequest(\"Delete citizen queue driver\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Temporary add MSP service request", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "c2ec515b-209f-4d91-81c1-c72a14600685", - "exec": [ - "// Run create tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "var svcReq = jsonData.service_request;", - "", - "// Set schema of the service_request property.", - "var schema = {", - " \"properties\" : {", - " \"periods\" : {", - " \"type\" : \"array\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service\" : {", - " \"type\" : \"object\"", - " },", - " \"sr_state\" : {", - " \"type\" : \"object\"", - " },", - " \"quantity\" : {", - " \"type\" : \"number\"", - " },", - " \"sr_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"citizen\" : {", - " \"type\" :[\"number\", \"object\"]", - " },", - " \"citizen_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"channel\" : {", - " \"type\" : \"object\"", - " },", - " \"channel_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " }", - " },", - " // \"required\" : [\"periods\", \"service_id\", \"sr_state_id\", \"service\"]", - " \"required\" : [\"periods\", \"service_id\", \"service\", \"sr_state\", \"quantity\", \"sr_id\",", - " \"citizen\", \"citizen_id\", \"channel\", \"channel_id\"]", - "};", - "", - "// Make sure the response is valid.", - "pm.test(\"Response has service_request property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"service_request\")).to.be.true;", - "});", - "pm.test(\"Response has errors property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"errors\")).to.be.true;", - "});", - "tests[\"Service_request property has correct schema\"] = tv4.validate(svcReq, schema);", - "", - "// Go back to the clear citizen driver.", - "postman.setNextRequest(\"Next citizen finish service\");", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : 1,\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "End clear queue via healthz endpoint", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Check citizen through queue (QT1)", - "item": [ - { - "name": "Check no citizens (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "9f95832a-57ef-4dcf-bdfe-916d2eb5d006", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "7896261d-7d88-4eba-860b-98472655c4a7", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "b72e7756-5de1-4a13-9609-8917ea63dee7", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "73110fdd-6476-403d-b6c9-c108032fbd66", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "66ca9683-be00-478b-92c0-6ac698d9088b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8a294876-edfa-4d5b-ada4-399f5339cd8f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8e9cd1ef-a5d9-4f81-b061-f818d4fc5603", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1b1f7f66-04cd-4f10-bfa2-c4f3fffe8715", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "818713b9-18a0-4fef-9c16-f71b43dd2ad0", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen begin-hold-finish (QT2)", - "item": [ - { - "name": "Check no citizens (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "3d9d9302-3b15-49f0-8bb2-3de1f4d134b8", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77d15de-91e2-4528-87bd-5734405b4def", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "} ", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "ce732be5-38a3-4825-90d1-4d52babf8917", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "13828662-748d-4b3f-9ead-454e795f1f01", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2a57d1b2-9d0c-4e07-ad20-d4dde457f9d1", - "exec": [ - "// Install postmanBDD, json-bigint parse and stringify.", - "eval(globals.postmanBDD);", - "eval(globals.json_bigint_parse);", - "", - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "4550a6b7-560d-48bf-bfc9-41453a8defec", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Place citizen on hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "dd51a48d-2e1e-47fd-978f-507b77a7901f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "place_on_hold", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Get service requests (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "f4809d17-b59d-47fc-861e-5edaf421bddc", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get periods for the first service request.", - " var allPeriods = allElements[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " var allPeriodCount = 0;", - "", - " // Find how many periods there are with null end time.", - " // Also, check schema.", - " allPeriods.forEach(function(onePeriod) {", - " ", - " // Find the open period.", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " ", - " });", - "} ", - "", - "// If there are some service requests, proceed with tests.", - "if (allElements !== null) {", - "", - " // Perform tests.", - " pm.test('There must be only one service request', function() {", - " pm.expect(allElements.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have three periods', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('Service request period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/service_requests/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "service_requests", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Call citizen from hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "66563797-2a92-4dbd-946e-7232dcac8260", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "ec865f9c-a4b6-421f-956e-783972df2ad3", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = allElements[0].service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen leave after create (QT3)", - "item": [ - { - "name": "Check no citizens (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "6cb5de42-277f-4c2d-b5ef-64b0f514d5bd", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "061a479c-37b4-4707-b8c6-c31cacadecec", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "16eaefc2-0d16-405e-9e70-e85314da841b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = null;", - " if (currentCitizen.service_reqs.length !== 0) {", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " }", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " if (allPeriods !== null) {", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - " }", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check citizen leave after waiting (QT4)", - "item": [ - { - "name": "Check no citizens (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "bc24cb66-e882-4b8a-b879-2d287078efab", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fcc1415c-0861-4516-8843-bfc3717a76d9", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "f26eaedb-5d41-4dcc-b856-aec599440601", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e0b48324-0ca2-4b2e-88ef-cbfbe10b5fb7", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1176fcca-52ff-4e13-b4a4-775ad324a4c6", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "f0aacc69-ff0e-4ed3-9ef7-f131bbf3a5bd", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "4ee90115-54c2-4b44-b6f1-d739e8a385b1", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check no citizens (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "6cb5de42-277f-4c2d-b5ef-64b0f514d5bd", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check update service information (QT5)", - "item": [ - { - "name": "Check no citizens (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "4dd23acf-1f43-42be-b965-0b11121f33f3", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var allOK = true;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Make sure it had a length of 0.", - " if (allElements.length !== 0) {", - " allOK = false;", - " }", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(allOK).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Create citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "d3bbd88a-25fd-4fe6-ae7e-359373df02ac", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "2be395b2-e14c-4ff8-8753-42ea1fe2010d", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// };", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2e7878d3-653e-479e-850f-c454b378217e", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3d906a10-51fe-4329-bac0-e69e6847e852", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "", - " // Save the service request ID for later.", - " var mySRId = allElements[0].service_reqs[0].sr_id;", - " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "ff3a25ca-0df2-40f2-b7a1-9e9b4bde2aab", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update quantity from 3 to 5 (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "153a139d-0daa-4031-ba0a-e7204ade2648", - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - "", - " // Perform tests.", - " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update service from PropTax to MSP (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8faaa693-a7a4-45ed-9318-325a498a98af", - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - "", - " // Perform tests.", - " pm.test('Updated service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "7a831dcf-ff2c-4907-95c5-bf042cdd967a", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (should be two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check pick qtxn customer (QT6)", - "item": [ - { - "name": "Check no citizens (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "9f95832a-57ef-4dcf-bdfe-916d2eb5d006", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "0aa4fd7b-529c-4f26-9998-c77d6c28eeb4", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(citizenCount).to.be.eql(0);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Create (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "cac82cd1-b2c1-4019-bf8f-bf8dfed473ef", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Edit (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "f52aea38-cdcb-4d0b-8421-0ee5b7486913", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "a0ded2ba-3a0d-45ff-ac3a-ee25ad2bd578", - "exec": [ - "// Install postmanBDD, json-bigint parse and stringify.", - "eval(globals.postmanBDD);", - "eval(globals.json_bigint_parse);", - "", - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Prop Tax via phone (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - List (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2eeb855a-cf49-4bab-abc4-739deef83d82", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Add to queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "c6bd4047-5ddb-4b96-9b1b-851455cbc71d", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Create (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "b5a2ef1c-c73c-4bc6-ba18-1370ba84bbe4", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Edit QTxn (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "f58d00cd-ab75-407b-ab4c-a541944510e3", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name_quick\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment_quick\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments,", - "// \"qt_xn_citizen_ind\" : 1", - "// };", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "12b50df4-9162-4d6d-8ec4-ddebc3cb6cda", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - "", - " // Perform tests.", - " pm.test(\"Should be updating single citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - MSP via email (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - List (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "6abcbbab-941e-43b4-85e8-6c2f14a19c75", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - " // Perform tests.", - " pm.test('Should be updating one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Add to queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "b396fdd7-845d-4702-ad69-1b53a2ced21b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should be adding one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check 2 citizens in queue (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "09e9a82d-d782-4916-b84d-970d41f0e231", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test('Must be two active citizens in the office', function() {", - " pm.expect(citizenCount).to.be.eql(2);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Set CSR to QTxn (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "09e9a82d-d782-4916-b84d-970d41f0e231", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", - " },", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"counter_id\": {{qtxn_id}}\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be second one (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "84ff5966-dfec-47d0-ae77-2c5819005fb9", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - begin serving (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "caecf715-4ce2-4422-8374-b330c0433f4b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - finish serving (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "d5890c30-3ce5-4807-9d17-bd58050dab44", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be first one (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "62e7f31d-0bd6-4337-bd70-f6a197063a00", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - citizen left (QT6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e502775f-6829-45f8-a225-61c54e625c7f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizen being served\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check pick non-qtxn customer (QT7)", - "item": [ - { - "name": "Check no citizens (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c9275086-164f-4f82-884f-75b5710f0b65", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "0aa4fd7b-529c-4f26-9998-c77d6c28eeb4", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test(\"There should be no citizens in the office\", function() {", - " pm.expect(citizenCount).to.be.eql(0);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Create (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "35c3241b-1fe5-423a-91bd-789ed675e61d", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Edit QTxn (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "b1833da7-f814-433f-8a11-295e244d9e4f", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1eac614b-0f6c-4b84-8423-de10ffec680f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - "", - " // Perform tests.", - " pm.test(\"Must be editing only one citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - MSP via email (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - List (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "f695dc9f-e14f-4e39-8cc0-06d6bff46d1d", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - "", - " // Perform tests.", - " pm.test('Must be editing only one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - Add to queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "4ed70849-8f22-4cd0-ab73-582dd012ba60", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be adding only one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{first_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Create (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "dde23c45-a5d4-4ff0-85a0-bac3873a8574", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should have been created\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Edit (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "03647497-dfc2-437d-bace-b6ab2a08fa7b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "9f22638b-3ebc-404e-a43b-fb8af32a2083", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Should be updating single citizen\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Prop Tax via phone (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - List (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "dcfc340a-b267-4a1b-8f39-cbedfe02ef76", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - "", - " // Perform tests.", - " pm.test('Should be updating one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - Add to queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "b82e3735-add5-4522-a418-a42931ba84ae", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should be adding one citizen to the queue', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{second_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Check 2 citizens in queue (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "09e9a82d-d782-4916-b84d-970d41f0e231", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenCount = 0;", - "", - "// If citizen property was present.", - "if (allElements !== null) {", - "", - " // Get number of citizens in the queue.", - " citizenCount = allElements.length;", - "}", - "", - "pm.test('Must be two active citizens in the office', function() {", - " pm.expect(citizenCount).to.be.eql(2);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Set CSR to Counter (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "09e9a82d-d782-4916-b84d-970d41f0e231", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", - " },", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"counter_id\": {{counter_id}}\n}" - }, - "url": { - "raw": "{{url}}csrs/{{current_csr_id}}/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "{{current_csr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be second one (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "cfcc38e7-765f-48aa-8600-159bbeda4994", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - begin serving (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "0ceb5800-311b-4b82-9278-80e3296843ba", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Second citizen - finish serving (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "ec44532d-3f42-41f4-8faa-7803c188970f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizen being served', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite next citizen - should be first one (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "a4812ded-5043-426a-b837-91ac5f06ba62", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Should only be inviting one citizen', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}", - "", - "// Store the ID of the citizen just invited.", - "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "First citizen - citizen left (QT7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "14a22c20-3902-4ca1-9e56-e97dde7b212b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizen being served\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test(\"Current citizen comments should be null\", function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.null;", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Setup Booking", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Authentication Token", - "event": [ - { - "listen": "test", - "script": { - "id": "454ea7d6-4c7e-4571-8bc1-be7eed57d03a", - "exec": [ - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " if (pm.expect(access_token).not.eql(null)){", - " pm.globals.set(\"token\", access_token);", - " }", - "});", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " if (pm.expect(refresh_token).not.eql(null)){", - " pm.globals.set(\"refresh_token\", refresh_token);", - " }", - "});", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " if (pm.expect(expires_in).not.eql(null)){", - " pm.globals.set(\"expires_in\", expires_in);", - " }", - "});", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " if (pm.expect(refresh_expires_in).not.eql(null)){", - " pm.globals.set(\"refresh_expires_in\", refresh_expires_in);", - " }", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/x-www-form-urlencoded", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - } - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.globals.set(\"token\", jsonData.access_token);\n\t pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n\t console.log(err ? err : res.json());\n\t} else {\n\t pm.globals.set(\"token\", 0);\n\t pm.globals.set(\"refresh_token\", 0);\n\t pm.globals.set(\"token_expires\", 0);\n\t pm.globals.set(\"refresh_token_expires\", 0);\n\t}\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// If no maximum response defined, set a default.\nresponse_max = 0;\nif (globals.response_max) {\n response_max = JSON.parse(globals.response_max);\n}\nelse {\n response_max = 5009;\n pm.globals.set(\"response_max\", JSON.stringify(response_max));\n};\n\n// Get the max response time allowed.\npm.test('Response time less than ' + response_max.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(response_max);\n});\n\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Type-Data", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"init_exam_type_data\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Get-Random", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_random_functions\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": \"object\" }\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_contact_information\": {},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": \"string\"},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilator\": {},\n \"invigilator_id\": {},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"sb_id\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_contact_information\", \"booking_id\", \"booking_name\", \"end_time\", \"fees\", \"room\", \"room_id\", \"start_time\", \"invigilator_id\", \"office\", \"office_id\"]\n },\n \"errors\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointment\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\", \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\", \"start_time\"],\n \"errors\": {}\n }\n },\n \"required\": [\"appointment\"],\n \"errors\": {}\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointments Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an appointment property.\npm.test(\"Response should have appointment property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"appointment\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"appointment\")) {\n\n //Test to see if service id has expected value\n pm.test(\"Validate Service ID has expected value\", function(){\n pm.expect(jsonData.service_id === environment.service_id);\n });\n\n //Test to see if office id has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if category has expected value\n pm.test(\"Validate category has expected value\", function(){\n pm.expect(jsonData.category === environment.category);\n });\n\n //Test to see if comments field has expected value\n pm.test(\"Validate comments has expected value\", function(){\n pm.expect(jsonData.comments === environment.comments);\n });\n\n //Test to see if citizen name field has expected value\n pm.test(\"Validate citizen name has expected value\", function(){\n pm.expect(jsonData.citizen_name === environment.citizne_name);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Appointment-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"appointment_list_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointments\": {\n \"type\": \"array\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\", \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\", \"start_time\"],\n \"errors\": {}\n }\n },\n \"required\": [\"appointments\"],\n \"errors\": {}\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointment List Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Room IDs", - "event": [ - { - "listen": "test", - "script": { - "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", - "exec": [ - "// Define the JSON Schema expected in response", - "var roomSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"rooms\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"capacity\": {\"type\": \"number\"},", - " \"color\": {\"type\": \"string\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"} ", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", - " }", - " }", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Room Schema\", function(){", - " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", - "});", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"rooms\")) {", - " console.log(\"==> Rooms\");", - " room_id_1 = jsonData.rooms[0].room_id;", - " room_id_2 = room_id_1;", - " if (jsonData.rooms.length > 1) {", - " room_id_2 = jsonData.rooms[1].room_id;", - " }", - " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", - " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "462566f5-d6cd-4c14-9b11-0cbf7abc0caf", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Service IDs", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3b11031e-4031-4569-91f2-49e23517ffee", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"services\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"number\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - " }", - " }", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Service Schema\", function(){", - " pm.expect(tv4.validate(jsonData, schema)).to.be.true;", - "});", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", - " pm.test(\"Validate Schema for \" + testTitle, function(){", - " pm.expect(tv4.validate(element, schema)).to.be.true;", - " });", - " ", - " // Test the authenticate response.", - " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", - " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", - " });", - " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", - " pm.expect(element.actual_service_ind).to.be.eql(1);", - " });", - " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the service IDs.", - "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ] - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver Booking", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Set health response time variable.", - "max_response_time = 1500;", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"max_response_time\", JSON.stringify(max_response_time));", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is health'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_response_time) {", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint Booking\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint Booking", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is health'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint Booking\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint Booking\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint Booking", - "event": [ - { - "listen": "test", - "script": { - "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints" - }, - { - "name": "Get Exam Types", - "item": [ - { - "name": "Exam Type List", - "event": [ - { - "listen": "test", - "script": { - "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", - "exec": [ - "// Define the JSON Schema expected in response", - "var examTypeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_color\": {\"type\": \"string\"},", - " \"exam_type_id\": {\"type\": \"number\"},", - " \"exam_type_name\": {\"type\": \"string\"},", - " \"ita_ind\": {\"type\": \"number\"},", - " \"method_type\": {\"type\": \"string\"},", - " \"number_of_hours\": {\"type\": \"number\"},", - " \"group_exam_ind\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", - " },", - " \"required\": []", - "}; ", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Exam Type Schema\", function(){", - " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", - "});", - "", - "// Store all exam type IDs for future use in adding exams", - "var allExamIds = [];", - "", - "// Make sure some data returned.", - "pm.test(\"Response has exam_types property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", - "});", - "pm.test(\"Response has at least one exam_type\", function(){", - " pm.expect(jsonData.exam_types.length).to.be.above(0);", - "});", - "", - "// Set up list of valid exam types, create random functions.", - "eval(environment.init_exam_type_data);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exam_types/", - "host": [ - "{{url}}exam_types" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Exams", - "item": [ - { - "name": "Exam Post End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", - "", - "// Store other variables for later use.", - "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", - "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", - "", - "// Calculate an expiry date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format expiry date for the exam.", - "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "", - "// If jsonData has an exam property, save exam ID.", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_list_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Put End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "3efcb554-1e5a-4e0a-91b8-aa32eb14a5e6", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Exams Export", - "item": [ - { - "name": "Exams Export List", - "event": [ - { - "listen": "test", - "script": { - "id": "9e02fbaf-627e-43db-b516-226b1a2874e8", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than 20,000ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(20000);", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "caf977c6-da22-4f6c-b0c9-ca5f0fc3ec29", - "exec": [ - "// Calculate a start date four weeks prior to today.", - "var start = new Date();", - "start.setDate(start.getDate()-28);", - "", - "// Get year, day, month from the start time.", - "start_year = start.getFullYear().toString();", - "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", - "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", - "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", - "", - "// Get year, day, month from the current day.", - "var today = new Date();", - "end_year = today.getFullYear().toString();", - "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", - "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", - "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", - "", - "console.log(\"Start: \" + start_date);", - "console.log(\"End: \" + end_date);", - "pm.globals.set(\"start_date\", start_date);", - "pm.globals.set(\"end_date\", end_date);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "export", - "" - ], - "query": [ - { - "key": "start_date", - "value": "{{start_date}}" - }, - { - "key": "end_date", - "value": "{{end_date}}" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Bookings", - "item": [ - { - "name": "Booking Post End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "a4099862-1c9a-48f7-adbf-0240e6eb185a", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "4df5dedd-4976-4cdc-b2be-739b2028cdc4", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "232f5232-de75-4f55-b999-2e39e39a12a8", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "59fc7e18-6df0-48d9-822c-6518fbece309", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Put End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "b127046f-b399-4e66-b76f-b6e79268b8ce", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9bde67e2-8eeb-4e50-90bf-caff1fef90a4", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point Again", - "event": [ - { - "listen": "test", - "script": { - "id": "b5e53be0-ddda-4c99-b563-5a3b449e0f79", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1e7c1d71-ea74-4a78-9ef2-ee9a7a47d915", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Invigilators", - "item": [ - { - "name": "Invigilator List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "0e68858e-985c-4fb0-bca6-04d33f65c28d", - "exec": [ - "// Define the JSON Schema expected in response", - "var invigilatorSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"invigilators\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"contact_email\": {\"type\": \"string\"},", - " \"contact_phone\": {\"type\": \"string\"},", - " \"contract_expiry_date\": {\"type\": \"string\"},", - " \"contract_number\": {\"type\": \"string\"},", - " \"invigilator_id\": {\"type\": \"number\"},", - " \"invigilator_name\": {\"type\": \"string\"},", - " \"invigilator_notes\": {\"type\": \"string\"},", - " \"office\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\", \"office\"]", - " }", - " }", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Invigilator Schema\", function(){", - " pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;", - "});", - "", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}invigilators/", - "host": [ - "{{url}}invigilators" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Rooms", - "item": [ - { - "name": "Room List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", - "exec": [ - "// Define the JSON Schema expected in response", - "var roomSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"rooms\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"capacity\": {\"type\": \"number\"},", - " \"color\": {\"type\": \"string\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"} ", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", - " }", - " }", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Room Schema\", function(){", - " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "CSRS", - "item": [ - { - "name": "CSRS Me Get End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1ed052a0-06cc-45c7-ae49-b19b96e02d51", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"back_office_list\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"actual_service_ind\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"display_dashboard_ind\": {\"type\": \"number\"},", - " \"parent\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"service_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"service_name\"]", - " },", - " \"parent_id\": {\"type\": \"number\"},", - " \"prefix\": {\"type\": \"string\"},", - " \"service_code\": {\"type\": \"string\"},", - " \"service_desc\": {\"type\": \"string\"},", - " \"service_id\": {\"type\": \"number\"},", - " \"service_name\": {\"type\": \"string\"},", - " },", - " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", - " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", - " \"service_name\"]", - " },", - " \"counters\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"counter_id\": {\"type\": \"number\"},", - " \"counter_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"counter_id\", \"counter_name\"]", - " },", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"quick_list\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"actual_service_ind\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"display_dashboard_ind\": {\"type\": \"number\"},", - " \"parent\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"service_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"service_name\"]", - " },", - " \"parent_id\": {\"type\": \"number\"},", - " \"prefix\": {\"type\": \"string\"},", - " \"service_code\": {\"type\": \"string\"},", - " \"service_desc\": {\"type\": \"string\"},", - " \"service_id\": {\"type\": \"number\"},", - " \"service_name\": {\"type\": \"string\"},", - " },", - " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", - " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", - " \"service_name\"]", - " },", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"},", - " \"timezone\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"timezone_id\": {\"type\": \"number\"},", - " \"timezone_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"timezone_id\", \"timezone_name\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"back_office_list\", \"counters\",", - " \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\",", - " \"quick_list\", \"sb\", \"sb_id\", ]", - " },", - " \"office_id\": {\"type\": \"number\"},", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " },", - " \"attention_needed\": {\"type\": \"boolean\"},", - " \"active_citizens\" : {\"type\": \"array\"},", - " \"back_office_display\": {\"type\": \"string\"},", - " \"recurring_feature_flag\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr\", \"attention_needed\", \"active_citizens\", \"back_office_display\", \"recurring_feature_flag\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "1c4020cd-fb05-478c-b908-3197c80d492e", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Offices", - "item": [ - { - "name": "Office List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "ad6ffb6d-efad-4e11-b4a9-99e49f457bbd", - "exec": [ - "// Define the JSON Schema expected in response", - "var officeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"offices\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"exams_enabled\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exams_enabled\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " }", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Office Schema\", function(){", - " pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}offices/", - "host": [ - "{{url}}offices" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Appointments", - "item": [ - { - "name": "Appointment Post End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "851b530c-92d7-482c-9e80-2a1f542b280c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e8444129-3a7b-4a2f-82d0-fd9b5d395f0d", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", - "pm.globals.set(\"citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}},\n \"citizen_name\": {{citizen_name}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid\\", - "eval(environment.appointment_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Put End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1cf958af-12dd-44f4-a624-d070cc6d90cb", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "52ba16f8-f08e-4a75-a64c-39e59c64c1c6", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"comments\", JSON.stringify(\"super EARLY\"));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}}\n}" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Public User Appointments", - "item": [ - { - "name": "Authenticate and create user", - "event": [ - { - "listen": "test", - "script": { - "id": "c8d8da75-f3d8-4d1f-83c7-cc41d2ad4317", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "", - "var user_id = jsonData[0].user_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"user_id\", JSON.stringify(user_id));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Typea", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n}" - }, - "url": { - "raw": "{{public_url}}users/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Book an appointment", - "event": [ - { - "listen": "test", - "script": { - "id": "851b530c-92d7-482c-9e80-2a1f542b280c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));", - "", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e8444129-3a7b-4a2f-82d0-fd9b5d395f0d", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", - "pm.globals.set(\"citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}}\n}" - }, - "url": { - "raw": "{{public_url}}appointments/", - "host": [ - "{{public_url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Edit user profile", - "event": [ - { - "listen": "test", - "script": { - "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"test@test.com\",\n \"telephone\": \"7787777777\",\n \"send_reminders\": true\n}" - }, - "url": { - "raw": "{{public_url}}users/{{user_id}}/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "{{user_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List all appointments", - "event": [ - { - "listen": "test", - "script": { - "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid\\", - "eval(environment.appointment_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{public_url}}users/appointments/", - "host": [ - "{{public_url}}users" - ], - "path": [ - "appointments", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Put End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1cf958af-12dd-44f4-a624-d070cc6d90cb", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "52ba16f8-f08e-4a75-a64c-39e59c64c1c6", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"comments\", JSON.stringify(\"super EARLY\"));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}}\n}" - }, - "url": { - "raw": "{{public_url}}appointments/{{appointment_id}}/", - "host": [ - "{{public_url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{public_user_token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{public_url}}appointments/{{appointment_id}}/", - "host": [ - "{{public_url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "id": "7f0ff870-49a0-4df2-9496-e6bf98e60a05", - "type": "text/javascript", - "exec": [ - "auth_url = globals.auth_url;", - "realm = globals.realm;", - "clientid = globals.clientid;", - "userid = globals.public_user_id;", - "password = globals.public_user_password;", - "client_secret = globals.client_secret;", - "", - "const echoPostRequest = {", - " url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',", - " method: 'POST',", - " header: 'Content-Type:application/x-www-form-urlencoded',", - " body: {", - " mode: 'raw',", - " raw: 'grant_type=password&client_id=' + clientid ", - " + '&username=' + userid ", - " + '&password=' + password", - " + '&client_secret=' + client_secret", - " }", - "};", - "pm.sendRequest(echoPostRequest, function (err, res) {", - " var jsonData = res.json();", - " if (jsonData.hasOwnProperty('access_token')) {", - " \tpm.globals.set(\"public_user_token\", jsonData.access_token);", - "\t pm.globals.set(\"public_user_refresh_token\", jsonData.refresh_token);", - "\t if (err) {", - "\t console.log(err);", - "\t }", - "\t // console.log(err ? err : res.json());", - "\t} else {", - "\t pm.globals.set(\"public_user_token\", 0);", - "\t pm.globals.set(\"public_user_refresh_token\", 0);", - "\t}", - "});" - ] - } - }, - { - "listen": "test", - "script": { - "id": "e331a1b0-a6b1-40a6-8a50-de4d357eecb6", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] - } - ] -} \ No newline at end of file + "info": { + "_postman_id": "1c30decd-7b40-4a76-aa2b-e092dc3eb9dc", + "name": "API_Test_TheQ_Booking", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Setup TheQ", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "id": "5409b66d-67a4-449b-8633-9aeca632b388", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Complex-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "f006b951-3205-4f21-a315-369f85591929", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"complex_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Create-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.expect(pm.response.code).to.eql(201);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 201) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 201 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Citizen-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"citizen_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var schema = {\n \"properties\" : {\n \"start_time\" : {\n \"type\" : \"string\"\n },\n \"citizen_name\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"number\", \"object\"]\n },\n \"qt_xn_citizen_ind\" : {\n \"type\" : \"number\"\n },\n \"ticket_number\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"service_reqs\" : {\n \"type\" : \"array\"\n },\n \"office_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"cs\" : {\n \"type\" : \"object\"\n },\n \"citizen_comments\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \n },\n \"required\" : [\"start_time\", \"citizen_name\", \"citizen_id\",\n \"qt_xn_citizen_ind\", \"ticket_number\", \"service_reqs\",\n \"office_id\", \"cs\", \"citizen_comments\"]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n \n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Service-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "0e6c4264-28b3-4a49-b6bb-1199aef2cb13", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"service_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var schema = {\n \"properties\" : {\n \"sr_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"sr_state\" : {\n \"type\" : \"object\"\n },\n \"periods\" : {\n \"type\" : \"array\"\n },\n \"service\" : {\n \"type\" : \"object\"\n },\n \"citizen\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"quantity\" : {\n \"type\" : \"number\"\n },\n \"service_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"channel\" : {\n \t\"type\" : \"object\"\n },\n \"channel_id\" : {\n \t\"type\" : [\"object\", \"number\"]\n }\n \n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Get-Active-Citizens-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "8856712d-73ca-4172-89ba-590e8b015c6b", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + } + ], + "description": "This folder performs basic authentication features." + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Set health response time variable.", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum load time allowed.", + "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints" + }, + { + "name": "Check user login", + "item": [ + { + "name": "Authenticate default QTxn user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "description": "Make sure the operator ID can log in" + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check channels", + "item": [ + { + "name": "Get channels", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "2b71673c-4aa2-47b3-8594-15f02b390ee0", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "42742c4c-505d-472d-857b-fdba74cc49ac", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"channel_name\" : {", + " \"type\" : \"string\"", + " },", + " \"channel_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " }", + " },", + " \"required\" : [\"channel_name\", \"channel_id\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allChannels = jsonData.channels;", + "var channelCount = 0;", + "var phoneId = 0;", + "var emailId = 0;", + "var phoneText = \"Phone\";", + "var emailText = \"Email/Fax/Mail\";", + "allChannels.forEach(function(channel) {", + " channelCount ++;", + " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", + " tests[testTitle] = tv4.validate(channel, schema);", + " if (channel.channel_name === phoneText) {", + " phoneId = channel.channel_id;", + " }", + " if (channel.channel_name === emailText) {", + " emailId = channel.channel_id;", + " }", + "});", + "", + "// Check that you found the phone ID.", + "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", + " pm.expect(phoneId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the email ID.", + "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", + " pm.expect(emailId).to.not.be.eql(0);", + "});", + "", + "// Store this ID for future use.", + "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", + "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}channels/", + "host": [ + "{{url}}channels" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check counters", + "item": [ + { + "name": "Store CSR and Office Info", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", + " },", + " \"office_name\": {\"type\": \"string\"},", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " \"attention_needed\": {\"type\": \"boolean\"},", + " \"required\": [\"csr\", \"attention_needed\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});", + "", + "// Make sure that jsonData has an csr property.", + "pm.test(\"Response should have csr property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", + "});", + "", + "var csr = 0;", + "var office = 0;", + "var counters = 0;", + "var counter_text = \"Counter\";", + "var counter_id = 0;", + "var qtxn_text = \"Quick Trans\";", + "var qtxn_id = 0;", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", + " ", + " csr = jsonData.csr;", + " counter_id = 0;", + " qtxn_id = 0;", + "", + " // Make sure that jsonData has an booking property.", + " pm.test(\"CSR should have office property\", function(){", + " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", + " });", + " ", + " // Make sure office has counter property.", + " if (csr.hasOwnProperty(\"office\")) {", + " office = csr.office;", + " ", + " // Make sure that jsonData has an booking property.", + " pm.test(\"Office should have counters property\", function(){", + " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", + " });", + "", + " // Make sure office has counter property.", + " if (office.hasOwnProperty(\"counters\")) {", + " counters = office.counters;", + " ", + " // Search for Counter and Quick Trans counters", + " counters.forEach(function(counter) {", + " if (counter.counter_name === counter_text) {", + " counter_id = counter.counter_id;", + " }", + " if (counter.counter_name === qtxn_text) {", + " qtxn_id = counter.counter_id;", + " }", + " });", + " ", + " // Make sure you found the right IDs.", + " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", + " pm.expect(counter_id).to.not.be.eql(0);", + " });", + " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", + " pm.expect(qtxn_id).to.not.be.eql(0);", + " });", + " ", + " // Store the ids for future use.", + " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", + " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", + " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", + " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", + " }", + "", + " ", + " }", + "", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check categories", + "item": [ + { + "name": "Get categories", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "e99f23ee-5a03-43d9-9075-75dbf06aadcc", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1a9c9ff5-8475-4dd2-a6c7-ffb5d4689145", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allCategories = jsonData.categories;", + "var categoryCount = 0;", + "allCategories.forEach(function(category) {", + " categoryCount ++;", + " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", + " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", + " var displayInd = category.display_dashboard_ind;", + " var serviceInd = category.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.eql(0);", + " });", + "", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(0);", + " });", + " ", + " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", + " pm.expect(category.parent_id).to.be.null;", + " });", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}categories/", + "host": [ + "{{url}}categories" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check services", + "item": [ + { + "name": "Get services", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3b11031e-4031-4569-91f2-49e23517ffee", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"number\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", + " tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);", + " displayInd = element.display_dashboard_ind;", + " serviceInd = element.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.within(0, 1);", + " });", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(1);", + " });", + " ", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the MSP service.", + "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the Property Tax service.", + "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Clear queue for tests", + "item": [ + { + "name": "Delete citizen queue driver", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "7f6d8d4e-e4fe-451f-a5d9-119fcefcd527", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "27f63d12-7c7c-42e1-9647-85480ee17cb8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Delete citizens, if there are any.", + " if (citizenIds.length > 0) {", + " ", + " // Set the current_client, to be deleted.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(citizenIds));", + " ", + " if (currentCitizen.service_reqs.length === 0) {", + " postman.setNextRequest(\"Next citizen left\");", + " // // Temporary kludge. Citizen left not working, so add SR, then delete.", + " // postman.setNextRequest(\"Temporary add MSP service request\");", + " }", + " else {", + " postman.setNextRequest(\"Next citizen finish service\");", + " }", + " }", + " ", + " // No more citizens. Clear the current, queue variables.", + " else {", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(\"\"));", + " postman.setEnvironmentVariable(\"current_queue\", JSON.stringify(\"\"));", + " postman.setNextRequest(\"End clear queue via healthz endpoint\");", + " }", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Next citizen finish service", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e516a2b7-0555-43b5-8058-ccd92b5b6e95", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "var citizenToBeDeleted = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "var citizenData = jsonData.citizen;", + "var testTitle = \"Check citizen finish service\";", + "", + "// Make sure the response is valid.", + "pm.test(testTitle + \": Response should have property 'citizen'\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"citizen\")).to.be.true;", + "});", + "pm.test(testTitle + \": Response should not have property 'message' indicating an error\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"message\")).to.be.false;", + "});", + "pm.test(testTitle + \": Citizen marked as finished should be citizen \" + citizenToBeDeleted.toString(), function(){", + " pm.expect(citizenData.citizen_id).to.be.eql(citizenToBeDeleted);", + "});", + "", + "// Go back to the delete citizen queue driver.", + "postman.setNextRequest(\"Delete citizen queue driver\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Next citizen citizen left", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "6caff547-493f-4f0d-a121-f8e3398f11c3", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform checks.", + " pm.test(\"Check there are no citizens waiting\", function(){", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test(\"Citizen that left must be \" + currentCitizen.citizen_id.toString() + \" (is \" + currentCitizenId.toString(), function(){", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + "}", + "", + "// Go back to the delete citizen queue driver.", + "postman.setNextRequest(\"Delete citizen queue driver\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Temporary add MSP service request", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"current_client\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "c2ec515b-209f-4d91-81c1-c72a14600685", + "exec": [ + "// Run create tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "var svcReq = jsonData.service_request;", + "", + "// Set schema of the service_request property.", + "var schema = {", + " \"properties\" : {", + " \"periods\" : {", + " \"type\" : \"array\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service\" : {", + " \"type\" : \"object\"", + " },", + " \"sr_state\" : {", + " \"type\" : \"object\"", + " },", + " \"quantity\" : {", + " \"type\" : \"number\"", + " },", + " \"sr_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"citizen\" : {", + " \"type\" :[\"number\", \"object\"]", + " },", + " \"citizen_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"channel\" : {", + " \"type\" : \"object\"", + " },", + " \"channel_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " }", + " },", + " // \"required\" : [\"periods\", \"service_id\", \"sr_state_id\", \"service\"]", + " \"required\" : [\"periods\", \"service_id\", \"service\", \"sr_state\", \"quantity\", \"sr_id\",", + " \"citizen\", \"citizen_id\", \"channel\", \"channel_id\"]", + "};", + "", + "// Make sure the response is valid.", + "pm.test(\"Response has service_request property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"service_request\")).to.be.true;", + "});", + "pm.test(\"Response has errors property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"errors\")).to.be.true;", + "});", + "tests[\"Service_request property has correct schema\"] = tv4.validate(svcReq, schema);", + "", + "// Go back to the clear citizen driver.", + "postman.setNextRequest(\"Next citizen finish service\");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : 1,\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "End clear queue via healthz endpoint", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Check citizen through queue (QT1)", + "item": [ + { + "name": "Check no citizens (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "9f95832a-57ef-4dcf-bdfe-916d2eb5d006", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "7896261d-7d88-4eba-860b-98472655c4a7", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "b72e7756-5de1-4a13-9609-8917ea63dee7", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "73110fdd-6476-403d-b6c9-c108032fbd66", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "66ca9683-be00-478b-92c0-6ac698d9088b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8a294876-edfa-4d5b-ada4-399f5339cd8f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8e9cd1ef-a5d9-4f81-b061-f818d4fc5603", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1b1f7f66-04cd-4f10-bfa2-c4f3fffe8715", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "818713b9-18a0-4fef-9c16-f71b43dd2ad0", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen begin-hold-finish (QT2)", + "item": [ + { + "name": "Check no citizens (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "3d9d9302-3b15-49f0-8bb2-3de1f4d134b8", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77d15de-91e2-4528-87bd-5734405b4def", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "} ", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "ce732be5-38a3-4825-90d1-4d52babf8917", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "13828662-748d-4b3f-9ead-454e795f1f01", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a57d1b2-9d0c-4e07-ad20-d4dde457f9d1", + "exec": [ + "// Install postmanBDD, json-bigint parse and stringify.", + "eval(globals.postmanBDD);", + "eval(globals.json_bigint_parse);", + "", + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "4550a6b7-560d-48bf-bfc9-41453a8defec", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Place citizen on hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "dd51a48d-2e1e-47fd-978f-507b77a7901f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "place_on_hold", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Get service requests (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "f4809d17-b59d-47fc-861e-5edaf421bddc", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get periods for the first service request.", + " var allPeriods = allElements[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " var allPeriodCount = 0;", + "", + " // Find how many periods there are with null end time.", + " // Also, check schema.", + " allPeriods.forEach(function(onePeriod) {", + " ", + " // Find the open period.", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " ", + " });", + "} ", + "", + "// If there are some service requests, proceed with tests.", + "if (allElements !== null) {", + "", + " // Perform tests.", + " pm.test('There must be only one service request', function() {", + " pm.expect(allElements.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have three periods', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('Service request period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/service_requests/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "service_requests", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Call citizen from hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "66563797-2a92-4dbd-946e-7232dcac8260", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ec865f9c-a4b6-421f-956e-783972df2ad3", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = allElements[0].service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen leave after create (QT3)", + "item": [ + { + "name": "Check no citizens (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "6cb5de42-277f-4c2d-b5ef-64b0f514d5bd", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "061a479c-37b4-4707-b8c6-c31cacadecec", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "16eaefc2-0d16-405e-9e70-e85314da841b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = null;", + " if (currentCitizen.service_reqs.length !== 0) {", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " }", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " if (allPeriods !== null) {", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + " }", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check citizen leave after waiting (QT4)", + "item": [ + { + "name": "Check no citizens (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "bc24cb66-e882-4b8a-b879-2d287078efab", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fcc1415c-0861-4516-8843-bfc3717a76d9", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "f26eaedb-5d41-4dcc-b856-aec599440601", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e0b48324-0ca2-4b2e-88ef-cbfbe10b5fb7", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1176fcca-52ff-4e13-b4a4-775ad324a4c6", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "f0aacc69-ff0e-4ed3-9ef7-f131bbf3a5bd", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "4ee90115-54c2-4b44-b6f1-d739e8a385b1", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check no citizens (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "6cb5de42-277f-4c2d-b5ef-64b0f514d5bd", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check update service information (QT5)", + "item": [ + { + "name": "Check no citizens (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "4dd23acf-1f43-42be-b965-0b11121f33f3", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2cbca8d4-50d9-4dde-9239-af3c987949b8", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var allOK = true;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Make sure it had a length of 0.", + " if (allElements.length !== 0) {", + " allOK = false;", + " }", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(allOK).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Create citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "d3bbd88a-25fd-4fe6-ae7e-359373df02ac", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "2be395b2-e14c-4ff8-8753-42ea1fe2010d", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// };", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2e7878d3-653e-479e-850f-c454b378217e", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3d906a10-51fe-4329-bac0-e69e6847e852", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "", + " // Save the service request ID for later.", + " var mySRId = allElements[0].service_reqs[0].sr_id;", + " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ff3a25ca-0df2-40f2-b7a1-9e9b4bde2aab", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update quantity from 3 to 5 (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"citizen_quantity_update\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "153a139d-0daa-4031-ba0a-e7204ade2648", + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + "", + " // Perform tests.", + " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update service from PropTax to MSP (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"service_MSP_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8faaa693-a7a4-45ed-9318-325a498a98af", + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + "", + " // Perform tests.", + " pm.test('Updated service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "7a831dcf-ff2c-4907-95c5-bf042cdd967a", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (should be two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check pick qtxn customer (QT6)", + "item": [ + { + "name": "Check no citizens (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "9f95832a-57ef-4dcf-bdfe-916d2eb5d006", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "0aa4fd7b-529c-4f26-9998-c77d6c28eeb4", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(citizenCount).to.be.eql(0);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Create (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "cac82cd1-b2c1-4019-bf8f-bf8dfed473ef", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Edit (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "f52aea38-cdcb-4d0b-8421-0ee5b7486913", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "a0ded2ba-3a0d-45ff-ac3a-ee25ad2bd578", + "exec": [ + "// Install postmanBDD, json-bigint parse and stringify.", + "eval(globals.postmanBDD);", + "eval(globals.json_bigint_parse);", + "", + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Prop Tax via phone (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"first_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - List (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2eeb855a-cf49-4bab-abc4-739deef83d82", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Add to queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "c6bd4047-5ddb-4b96-9b1b-851455cbc71d", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Create (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "b5a2ef1c-c73c-4bc6-ba18-1370ba84bbe4", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/1/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "1", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Edit QTxn (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "f58d00cd-ab75-407b-ab4c-a541944510e3", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\",\"citizen_name_quick\",\"citizen_comment_quick\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name_quick\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment_quick\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments,", + "// \"qt_xn_citizen_ind\" : 1", + "// };", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "12b50df4-9162-4d6d-8ec4-ddebc3cb6cda", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + "", + " // Perform tests.", + " pm.test(\"Should be updating single citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - MSP via email (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"second_client\",\"citizen_quantity_update\",\"channel_email_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - List (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "6abcbbab-941e-43b4-85e8-6c2f14a19c75", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + " // Perform tests.", + " pm.test('Should be updating one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Add to queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "b396fdd7-845d-4702-ad69-1b53a2ced21b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should be adding one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check 2 citizens in queue (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "09e9a82d-d782-4916-b84d-970d41f0e231", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test('Must be two active citizens in the office', function() {", + " pm.expect(citizenCount).to.be.eql(2);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Set CSR to QTxn (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"operator_token\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "09e9a82d-d782-4916-b84d-970d41f0e231", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", + " },", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"counter_id\": {{qtxn_id}}\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be second one (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "84ff5966-dfec-47d0-ae77-2c5819005fb9", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - begin serving (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "caecf715-4ce2-4422-8374-b330c0433f4b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - finish serving (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "d5890c30-3ce5-4807-9d17-bd58050dab44", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be first one (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "62e7f31d-0bd6-4337-bd70-f6a197063a00", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - citizen left (QT6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e502775f-6829-45f8-a225-61c54e625c7f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizen being served\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check pick non-qtxn customer (QT7)", + "item": [ + { + "name": "Check no citizens (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c9275086-164f-4f82-884f-75b5710f0b65", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "0aa4fd7b-529c-4f26-9998-c77d6c28eeb4", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test(\"There should be no citizens in the office\", function() {", + " pm.expect(citizenCount).to.be.eql(0);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Create (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "35c3241b-1fe5-423a-91bd-789ed675e61d", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"first_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/0/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "0", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Edit QTxn (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "b1833da7-f814-433f-8a11-295e244d9e4f", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\",\"citizen_name_quick\",\"citizen_comment_quick\",\"qtxn_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1eac614b-0f6c-4b84-8423-de10ffec680f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + "", + " // Perform tests.", + " pm.test(\"Must be editing only one citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name_quick}},\n \"citizen_comments\" : {{citizen_comment_quick}},\n \"qt_xn_citizen_ind\" : 1,\n \"counter_id\": {{qtxn_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - MSP via email (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_MSP_id\",\"first_client\",\"citizen_quantity_update\",\"channel_email_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_MSP_id}},\n\t\t\"citizen_id\" : {{first_client}},\n\t\t\"quantity\" : {{citizen_quantity_update}},\n\t\t\"channel_id\" : {{channel_email_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - List (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "f695dc9f-e14f-4e39-8cc0-06d6bff46d1d", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + "", + " // Perform tests.", + " pm.test('Must be editing only one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - Add to queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"first_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "4ed70849-8f22-4cd0-ab73-582dd012ba60", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be adding only one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{first_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{first_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Create (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "dde23c45-a5d4-4ff0-85a0-bac3873a8574", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should have been created\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"second_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/1/add_citizen/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "1", + "add_citizen", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Edit (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "03647497-dfc2-437d-bace-b6ab2a08fa7b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "9f22638b-3ebc-404e-a43b-fb8af32a2083", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Should be updating single citizen\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}},\n \"counter_id\": {{counter_id}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Prop Tax via phone (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"second_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{second_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - List (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "dcfc340a-b267-4a1b-8f39-cbedfe02ef76", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + "", + " // Perform tests.", + " pm.test('Should be updating one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - Add to queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"second_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "b82e3735-add5-4522-a418-a42931ba84ae", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should be adding one citizen to the queue', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{second_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{second_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Check 2 citizens in queue (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "09e9a82d-d782-4916-b84d-970d41f0e231", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenCount = 0;", + "", + "// If citizen property was present.", + "if (allElements !== null) {", + "", + " // Get number of citizens in the queue.", + " citizenCount = allElements.length;", + "}", + "", + "pm.test('Must be two active citizens in the office', function() {", + " pm.expect(citizenCount).to.be.eql(2);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Set CSR to Counter (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "070b3e97-88a9-4e82-ae4e-cf476625b4b9", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_csr_id\",\"operator_token\",\"counter_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "09e9a82d-d782-4916-b84d-970d41f0e231", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", + " },", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"counter_id\": {{counter_id}}\n}" + }, + "url": { + "raw": "{{url}}csrs/{{current_csr_id}}/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "{{current_csr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be second one (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "cfcc38e7-765f-48aa-8600-159bbeda4994", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - begin serving (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "0ceb5800-311b-4b82-9278-80e3296843ba", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Second citizen - finish serving (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ec44532d-3f42-41f4-8faa-7803c188970f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"second_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizen being served', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite next citizen - should be first one (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "a4812ded-5043-426a-b837-91ac5f06ba62", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Should only be inviting one citizen', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}", + "", + "// Store the ID of the citizen just invited.", + "postman.setEnvironmentVariable(\"current_client\", JSON.stringify(currentCitizenId));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "First citizen - citizen left (QT7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "14a22c20-3902-4ca1-9e56-e97dde7b212b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name_quick\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment_quick\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_email_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"first_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizen being served\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test(\"Current citizen comments should be null\", function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.null;", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Setup Booking", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "id": "5409b66d-67a4-449b-8633-9aeca632b388", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Authentication Token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "454ea7d6-4c7e-4571-8bc1-be7eed57d03a", + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + } + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Type-Data", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"init_exam_type_data\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Get-Random", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_random_functions\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": \"object\" }\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_contact_information\": {},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": \"string\"},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilator\": {},\n \"invigilator_id\": {},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"sb_id\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_contact_information\", \"booking_id\", \"booking_name\", \"end_time\", \"fees\", \"room\", \"room_id\", \"start_time\", \"invigilator_id\", \"office\", \"office_id\"]\n },\n \"errors\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointment\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\": \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": [\"null\", \"number\"]},\n \"start_time\": {\"type\": \"string\"},\n \"blackout_flag\": {\"type\": \"string\"},\n \"citizen_id\": {\"type\": \"number\"},\n \"online_flag\": {\"type\": \"boolean\"},\n \"recurring_uuid\": {\"type\": [\"null\", \"string\"]},\n \"services\": {\n \"type\": \"object\",\n \"properties\": {\n \"deleted\": {\"type\": [\"null\", \"string\"]},\n \"display_dashboard_ind\": {\"type\": \"number\"},\n \"timeslot_duration\": {\"type\": [\"null\", \"number\"]},\n \"parent\": {\n \"type\": \"object\",\n \"properties\": {\n \"service_name\": {\"type\": \"string\"}\n },\n \"required\": [\"service_name\"]\n },\n \"service_name\": {\"type\": \"string\"},\n \"service_code\": {\"type\": \"string\"},\n \"actual_service_ind\": {\"type\": \"number\"},\n \"online_link\": {\"type\": [\"null\", \"string\"]},\n \"service_desc\": {\"type\": \"string\"},\n \"service_id\": {\"type\": \"number\"},\n \"parent_id\": {\"type\": \"number\"},\n \"prefix\": {\"type\": \"string\"},\n \"online_availability\": {\"type\": [\"null\", \"string\"]},\n \"external_service_name\": {\"type\": [\"null\", \"string\"]}\n },\n \"required\": [\"deleted\", \"display_dashboard_ind\", \"timeslot_duration\", \"parent\",\n \"service_name\", \"service_code\", \"actual_service_ind\", \"online_link\",\n \"service_desc\", \"service_id\", \"parent_id\", \"prefix\", \"online_availability\",\n \"external_service_name\"]\n }\n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\",\n \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\",\n \"start_time\", \"blackout_flag\", \"citizen_id\", \"online_flag\",\n \"recurring_uuid\", \"service\"],\n },\n \"errors\": {\"type\": [\"object\", \"string\"]}\n },\n \"required\": [\"appointment\", \"errors\"],\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointments Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an appointment property.\npm.test(\"Response should have appointment property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"appointment\")).to.be.true;\n});\n\nvar name = jsonData.appointment.citizen_name;\nvar comments = jsonData.appointment.comments;\nvar contact = jsonData.appointment.contact_information;\nvar name_expect = JSON.parse(pm.globals.get(\"appt_citizen_name\"));\nvar comments_expect = JSON.parse(pm.globals.get(\"appt_comments\"));\nvar contact_expect = JSON.parse(pm.globals.get(\"appt_contact_information\"));\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"appointment\")) {\n\n //Test to see if service id has expected value\n pm.test(\"Validate Service ID has expected value\", function(){\n pm.expect(jsonData.service_id === environment.service_id);\n });\n\n //Test to see if office id has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if category has expected value\n pm.test(\"Validate category has expected value\", function(){\n pm.expect(jsonData.category === environment.category);\n });\n\n //Test to see if citizen name field has expected value\n pm.test(\"Name should be '\" + name_expect + \"', it is '\" + name + \"'\", function(){\n pm.expect(name).to.be.eql(name_expect);\n });\n //Test to see if comments field has expected value\n pm.test(\"Comments should be '\" + comments_expect + \"'; it is '\" + comments +\"'\", function(){\n pm.expect(comments).to.be.eql(comments_expect);\n });\n //Test to see if contact info field has expected value\n pm.test(\"Contact should be '\" + contact_expect + \"'; it is '\" + contact +\"'\", function(){\n pm.expect(contact).to.be.eql(contact_expect);\n });\n};\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Appointment-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"appointment_list_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar appointmentSchema = {\n \"type\": \"object\", \n \"properties\": {\n \"appointments\": {\n \"type\": \"array\",\n \"properties\": {\n \"appointment_id\": {\"type\": \"number\"},\n \"checked_in_time\": {\"type\": [\"string\", \"null\"]},\n \"citizen_name\": {\"type\": \"string\"},\n \"comments\": {\"type\": \"string\"},\n \"contact_information\": {},\n \"end_time\": {\"type\": \"string\"},\n \"office\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"service_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n \n },\n \"required\": [\"appointment_id\", \"checked_in_time\", \"citizen_name\", \"comments\", \"contact_information\", \"end_time\", \"office\", \"office_id\", \"service_id\", \"start_time\"],\n \"errors\": {}\n }\n },\n \"required\": [\"appointments\"],\n \"errors\": {}\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Appointment List Schema\", function(){\n pm.expect(tv4.validate(jsonData, appointmentSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Room IDs", + "event": [ + { + "listen": "test", + "script": { + "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", + "exec": [ + "// Define the JSON Schema expected in response", + "var roomSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"rooms\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"capacity\": {\"type\": \"number\"},", + " \"color\": {\"type\": \"string\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"} ", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", + " }", + " }", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Room Schema\", function(){", + " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", + "});", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"rooms\")) {", + " console.log(\"==> Rooms\");", + " room_id_1 = jsonData.rooms[0].room_id;", + " room_id_2 = room_id_1;", + " if (jsonData.rooms.length > 1) {", + " room_id_2 = jsonData.rooms[1].room_id;", + " }", + " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", + " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "462566f5-d6cd-4c14-9b11-0cbf7abc0caf", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Service IDs", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3b11031e-4031-4569-91f2-49e23517ffee", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"services\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"number\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + " }", + " }", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Service Schema\", function(){", + " pm.expect(tv4.validate(jsonData, schema)).to.be.true;", + "});", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", + " pm.test(\"Validate Schema for \" + testTitle, function(){", + " pm.expect(tv4.validate(element, schema)).to.be.true;", + " });", + " ", + " // Test the authenticate response.", + " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", + " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", + " });", + " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", + " pm.expect(element.actual_service_ind).to.be.eql(1);", + " });", + " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the service IDs.", + "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ] + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver Booking", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Set health response time variable.", + "max_response_time = 1500;", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"max_response_time\", JSON.stringify(max_response_time));", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is health'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_response_time) {", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint Booking\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint Booking", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is health'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint Booking\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint Booking\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint Booking", + "event": [ + { + "listen": "test", + "script": { + "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints" + }, + { + "name": "Get Exam Types", + "item": [ + { + "name": "Exam Type List", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", + "exec": [ + "// Define the JSON Schema expected in response", + "var examTypeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_color\": {\"type\": \"string\"},", + " \"exam_type_id\": {\"type\": \"number\"},", + " \"exam_type_name\": {\"type\": \"string\"},", + " \"ita_ind\": {\"type\": \"number\"},", + " \"method_type\": {\"type\": \"string\"},", + " \"number_of_hours\": {\"type\": \"number\"},", + " \"group_exam_ind\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", + " },", + " \"required\": []", + "}; ", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Exam Type Schema\", function(){", + " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", + "});", + "", + "// Store all exam type IDs for future use in adding exams", + "var allExamIds = [];", + "", + "// Make sure some data returned.", + "pm.test(\"Response has exam_types property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", + "});", + "pm.test(\"Response has at least one exam_type\", function(){", + " pm.expect(jsonData.exam_types.length).to.be.above(0);", + "});", + "", + "// Set up list of valid exam types, create random functions.", + "eval(environment.init_exam_type_data);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exam_types/", + "host": [ + "{{url}}exam_types" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Exams", + "item": [ + { + "name": "Exam Post End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", + "// Store other variables for later use.", + "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", + "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", + "// Calculate an expiry date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format expiry date for the exam.", + "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "", + "// If jsonData has an exam property, save exam ID.", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_list_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Put End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"exam_method\",\"random_exam_type_id\",\"exam_written_ind\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3efcb554-1e5a-4e0a-91b8-aa32eb14a5e6", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Exams Export", + "item": [ + { + "name": "Exams Export List", + "event": [ + { + "listen": "test", + "script": { + "id": "9e02fbaf-627e-43db-b516-226b1a2874e8", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than 20,000ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(20000);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "caf977c6-da22-4f6c-b0c9-ca5f0fc3ec29", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date four weeks prior to today.", + "var start = new Date();", + "start.setDate(start.getDate()-28);", + "// Get year, day, month from the start time.", + "start_year = start.getFullYear().toString();", + "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", + "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", + "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", + "// Get year, day, month from the current day.", + "var today = new Date();", + "end_year = today.getFullYear().toString();", + "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", + "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", + "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", + "console.log(\"Start: \" + start_date);", + "console.log(\"End: \" + end_date);", + "pm.globals.set(\"start_date\", start_date);", + "pm.globals.set(\"end_date\", end_date);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "export", + "" + ], + "query": [ + { + "key": "start_date", + "value": "{{start_date}}" + }, + { + "key": "end_date", + "value": "{{end_date}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Bookings", + "item": [ + { + "name": "Booking Post End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "a4099862-1c9a-48f7-adbf-0240e6eb185a", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "4df5dedd-4976-4cdc-b2be-739b2028cdc4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "232f5232-de75-4f55-b999-2e39e39a12a8", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "59fc7e18-6df0-48d9-822c-6518fbece309", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Put End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "b127046f-b399-4e66-b76f-b6e79268b8ce", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9bde67e2-8eeb-4e50-90bf-caff1fef90a4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point Again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "b5e53be0-ddda-4c99-b563-5a3b449e0f79", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1e7c1d71-ea74-4a78-9ef2-ee9a7a47d915", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Invigilators", + "item": [ + { + "name": "Invigilator List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "0e68858e-985c-4fb0-bca6-04d33f65c28d", + "exec": [ + "// Define the JSON Schema expected in response", + "var invigilatorSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"invigilators\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"contact_email\": {\"type\": \"string\"},", + " \"contact_phone\": {\"type\": \"string\"},", + " \"contract_expiry_date\": {\"type\": \"string\"},", + " \"contract_number\": {\"type\": \"string\"},", + " \"invigilator_id\": {\"type\": \"number\"},", + " \"invigilator_name\": {\"type\": \"string\"},", + " \"invigilator_notes\": {\"type\": \"string\"},", + " \"office\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\", \"office\"]", + " }", + " }", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Invigilator Schema\", function(){", + " pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}invigilators/", + "host": [ + "{{url}}invigilators" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Rooms", + "item": [ + { + "name": "Room List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", + "exec": [ + "// Define the JSON Schema expected in response", + "var roomSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"rooms\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"capacity\": {\"type\": \"number\"},", + " \"color\": {\"type\": \"string\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"} ", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", + " }", + " }", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Room Schema\", function(){", + " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "CSRS", + "item": [ + { + "name": "CSRS Me Get End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "1ed052a0-06cc-45c7-ae49-b19b96e02d51", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"back_office_list\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"actual_service_ind\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"display_dashboard_ind\": {\"type\": \"number\"},", + " \"parent\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"service_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"service_name\"]", + " },", + " \"parent_id\": {\"type\": \"number\"},", + " \"prefix\": {\"type\": \"string\"},", + " \"service_code\": {\"type\": \"string\"},", + " \"service_desc\": {\"type\": \"string\"},", + " \"service_id\": {\"type\": \"number\"},", + " \"service_name\": {\"type\": \"string\"},", + " },", + " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", + " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", + " \"service_name\"]", + " },", + " \"counters\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"counter_id\": {\"type\": \"number\"},", + " \"counter_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"counter_id\", \"counter_name\"]", + " },", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"quick_list\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"actual_service_ind\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"display_dashboard_ind\": {\"type\": \"number\"},", + " \"parent\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"service_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"service_name\"]", + " },", + " \"parent_id\": {\"type\": \"number\"},", + " \"prefix\": {\"type\": \"string\"},", + " \"service_code\": {\"type\": \"string\"},", + " \"service_desc\": {\"type\": \"string\"},", + " \"service_id\": {\"type\": \"number\"},", + " \"service_name\": {\"type\": \"string\"},", + " },", + " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", + " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", + " \"service_name\"]", + " },", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"},", + " \"timezone\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"timezone_id\": {\"type\": \"number\"},", + " \"timezone_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"timezone_id\", \"timezone_name\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"back_office_list\", \"counters\",", + " \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\",", + " \"quick_list\", \"sb\", \"sb_id\", ]", + " },", + " \"office_id\": {\"type\": \"number\"},", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " },", + " \"attention_needed\": {\"type\": \"boolean\"},", + " \"active_citizens\" : {\"type\": \"array\"},", + " \"back_office_display\": {\"type\": \"string\"},", + " \"recurring_feature_flag\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr\", \"attention_needed\", \"active_citizens\", \"back_office_display\", \"recurring_feature_flag\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "1c4020cd-fb05-478c-b908-3197c80d492e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Offices", + "item": [ + { + "name": "Office List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ad6ffb6d-efad-4e11-b4a9-99e49f457bbd", + "exec": [ + "// Define the JSON Schema expected in response", + "var officeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"offices\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"exams_enabled\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exams_enabled\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " }", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Office Schema\", function(){", + " pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}offices/", + "host": [ + "{{url}}offices" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Appointments", + "item": [ + { + "name": "Appointment Post End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"My contact info\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid\\", + "eval(environment.appointment_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Put End-point", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\",\"current_office_id\",\"category\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"super EARLY\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}}\n}" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Public User Appointments", + "item": [ + { + "name": "Authenticate public user token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"public_user_id\",\"public_user_password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"public_user_token\", jsonData.access_token);", + "pm.globals.set(\"public_user_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{public_user_id}}&password={{public_user_password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ] + } + }, + "response": [] + }, + { + "name": "Authenticate and create user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"clientid\",\"public_user_id\",\"public_user_password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "console.log(firstUser)", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "", + "//Dynamic variables used for end-point testing later on", + "var user_id = firstUser.user_id;", + "var user_phone = firstUser.telephone;", + "postman.setEnvironmentVariable(\"user_id\", JSON.stringify(user_id));", + "postman.setEnvironmentVariable(\"user_phone\", JSON.stringify(user_phone));", + "pm.test(\"Stored user_id for later public user requests\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"user_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Typea", + "type": "text", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{public_user_id}}&password={{public_user_password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{public_url}}users/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Discover public appointment office", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var offices = jsonData.offices || [];", + "var office = offices.find(function(candidate) {", + " return candidate.office_name === \"100 Mile House\";", + "});", + "", + "pm.test(\"Public appointment office was discovered\", function(){", + " pm.expect(office).to.not.be.oneOf([null, undefined]);", + "});", + "", + "pm.environment.set(\"public_office_id\", String(office.office_id));", + "pm.environment.set(\"public_office_timezone\", office.timezone.timezone_name);" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}offices/", + "host": [ + "{{public_url}}offices" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Discover public appointment service", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var services = jsonData.services || [];", + "var service = services.find(function(candidate) {", + " return candidate.service_name === \"Deferment Application\";", + "});", + "", + "pm.test(\"Public appointment service was discovered\", function(){", + " pm.expect(service).to.not.be.oneOf([null, undefined]);", + "});", + "", + "pm.environment.set(\"public_service_id\", String(service.service_id));" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}services/", + "host": [ + "{{public_url}}services" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Find available public appointment slot", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"public_office_id\",\"public_service_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "var slotDay = null;", + "var slot = null;", + "", + "Object.keys(jsonData).forEach(function(dayKey) {", + " if (!slotDay && Array.isArray(jsonData[dayKey]) && jsonData[dayKey].length > 0) {", + " slotDay = dayKey;", + " slot = jsonData[dayKey][0];", + " }", + "});", + "", + "pm.test(\"At least one public appointment slot is available\", function(){", + " pm.expect(slotDay).to.not.be.oneOf([null, undefined, \"\"]);", + " pm.expect(slot).to.not.be.oneOf([null, undefined]);", + "});", + "", + "var timezoneName = pm.environment.get(\"public_office_timezone\") || \"America/Creston\";", + "var offset = timezoneName === \"America/Creston\" ? \"-07:00\" : \"-07:00\";", + "var parts = slotDay.split(\"/\");", + "var dayIso = [parts[2], parts[0], parts[1]].join(\"-\");", + "var startIso = dayIso + \"T\" + slot.start_time + \":00\" + offset;", + "var endIso = dayIso + \"T\" + slot.end_time + \":00\" + offset;", + "", + "pm.globals.set(\"public_start_time\", JSON.stringify(startIso));", + "pm.globals.set(\"public_end_time\", JSON.stringify(endIso));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"public_service_id\"));", + "pm.globals.set(\"appt_comments\", JSON.stringify(\"My self serve appt.\"));", + "pm.globals.set(\"appt_citizen_name\", JSON.stringify(\"cfms-postman-public-user\"));", + "pm.globals.set(\"appt_contact_information\", JSON.stringify(\"test@test.com\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}offices/{{public_office_id}}/slots/?service_id={{public_service_id}}", + "host": [ + "{{public_url}}offices" + ], + "path": [ + "{{public_office_id}}", + "slots", + "" + ], + "query": [ + { + "key": "service_id", + "value": "{{public_service_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Book an appointment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "", + "// Update the comments to include phone number, before data test.", + "var comments_old = JSON.parse(pm.globals.get(\"appt_comments\"));", + "var user_phone = JSON.parse(postman.getEnvironmentVariable(\"user_phone\"));", + "var comments_new = comments_old + '. Phone: ' + user_phone;", + "console.log(\"==> Old: \" + comments_old);", + "console.log(\"==> Phone: \" + user_phone);", + "console.log(\"==> New: \" + comments_new);", + "pm.globals.set(\"appt_comments\", JSON.stringify(comments_new));", + "", + "// Now ready for the data check.", + "eval(environment.appointment_data_check);", + "", + "//Dynamic variable used for end-point testing later on", + "var appointment_id = jsonData.appointment.appointment_id;", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));", + "pm.test(\"Stored appointment_id for public appointment cleanup\", function(){", + " pm.expect(postman.getEnvironmentVariable(\"appointment_id\")).to.not.be.oneOf([null, undefined, \"\"]);", + "});", + "", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"service_id\",\"public_office_id\",\"public_start_time\",\"public_end_time\",\"category\",\"appt_comments\",\"appt_citizen_name\",\"appt_contact_information\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "var requiredVars = [\"public_start_time\", \"public_end_time\", \"service_id\"];", + "requiredVars.forEach(function(varName) {", + " pm.test(\"Required variable \" + varName + \" is set before public booking\", function(){", + " pm.expect(pm.globals.get(varName)).to.not.be.oneOf([null, undefined, \"\"]);", + " });", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{public_office_id}},\n \"start_time\": {{public_start_time}},\n \"end_time\": {{public_end_time}},\n \"category\": {{category}},\n \"comments\": {{appt_comments}},\n \"citizen_name\": {{appt_citizen_name}},\n \"contact_information\": {{appt_contact_information}}\n}" + }, + "url": { + "raw": "{{public_url}}appointments/", + "host": [ + "{{public_url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Edit user profile", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"user_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"test@test.com\",\n \"telephone\": \"0000000000\",\n \"send_email_reminders\": true,\n \"send_sms_reminders\": true\n}" + }, + "url": { + "raw": "{{public_url}}users/{{user_id}}/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "{{user_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get user profile", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "var allSchema = {", + " \"type\": \"array\",", + " \"items\": {\"type\": \"object\"}", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate All Users Schema\", function(){", + " pm.expect(tv4.validate(jsonData, allSchema)).to.be.true;", + "});", + "", + "var firstUser = jsonData[0];", + "", + "var userSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"email\": {\"type\": \"string\"},", + " \"last_name\": {\"type\": \"string\"},", + " \"user_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"},", + " \"send_email_reminders\": {\"type\": \"boolean\"},", + " \"send_sms_reminders\": {\"type\": \"boolean\"},", + " \"display_name\": {\"type\": \"string\"},", + " \"telephone\": {\"type\": \"string\"}", + " },", + " \"required\": [\"email\", \"last_name\", \"user_id\", \"username\", \"send_email_reminders\", \"send_sms_reminders\",", + " \"display_name\", \"telephone\"]", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate First User Schema\", function(){", + " pm.expect(tv4.validate(firstUser, userSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "", + "type": "text", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{public_url}}users/me/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "List all appointments", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid\\", + "eval(environment.appointment_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{public_url}}users/appointments/", + "host": [ + "{{public_url}}users" + ], + "path": [ + "appointments", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"public_url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.max_response_time);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{public_user_token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{public_url}}appointments/{{appointment_id}}/", + "host": [ + "{{public_url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ] +} diff --git a/api/postman/Load_Test_Booking.json b/api/postman/Load_Test_Booking.json index 4fee3a530..7b6a9f7e1 100644 --- a/api/postman/Load_Test_Booking.json +++ b/api/postman/Load_Test_Booking.json @@ -1,2794 +1,3284 @@ { - "info": { - "_postman_id": "a0066296-ea47-4c6a-834a-ec58a02f8dea", - "name": "Load_Test_Booking", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Setup", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Authentication Token", - "event": [ - { - "listen": "test", - "script": { - "id": "454ea7d6-4c7e-4571-8bc1-be7eed57d03a", - "exec": [ - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " if (pm.expect(access_token).not.eql(null)){", - " pm.globals.set(\"token\", access_token);", - " }", - "});", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " if (pm.expect(refresh_token).not.eql(null)){", - " pm.globals.set(\"refresh_token\", refresh_token);", - " }", - "});", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " if (pm.expect(expires_in).not.eql(null)){", - " pm.globals.set(\"expires_in\", expires_in);", - " }", - "});", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " if (pm.expect(refresh_expires_in).not.eql(null)){", - " pm.globals.set(\"refresh_expires_in\", refresh_expires_in);", - " }", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "value": "application/x-www-form-urlencoded", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - } - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// If no maximum response defined, set a default.\nresponse_max = 0;\nif (globals.response_max) {\n response_max = JSON.parse(globals.response_max);\n}\nelse {\n response_max = 5009;\n pm.globals.set(\"response_max\", JSON.stringify(response_max));\n};\n\n// Get the max response time allowed.\npm.test('Response time less than ' + response_max.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(response_max);\n});\n\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Type-Data", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"init_exam_type_data\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Get-Random", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_random_functions\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": \"object\" }\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Booking-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_contact_information\": {},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": \"string\"},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilator\": {},\n \"invigilator_id\": {},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"sb_id\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_contact_information\", \"booking_id\", \"booking_name\", \"end_time\", \"fees\", \"room\", \"room_id\", \"start_time\", \"invigilator_id\", \"office\", \"office_id\"]\n },\n \"errors\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Room IDs", - "event": [ - { - "listen": "test", - "script": { - "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", - "exec": [ - "// Define the JSON Schema expected in response", - "var roomSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"rooms\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"capacity\": {\"type\": \"number\"},", - " \"color\": {\"type\": \"string\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"} ", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", - " }", - " }", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Room Schema\", function(){", - " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", - "});", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"rooms\")) {", - " console.log(\"==> Rooms\");", - " room_id_1 = jsonData.rooms[0].room_id;", - " room_id_2 = room_id_1;", - " if (jsonData.rooms.length > 1) {", - " room_id_2 = jsonData.rooms[1].room_id;", - " }", - " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", - " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "462566f5-d6cd-4c14-9b11-0cbf7abc0caf", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Get Service IDs", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3b11031e-4031-4569-91f2-49e23517ffee", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"services\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"number\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - " }", - " }", - "};", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Service Schema\", function(){", - " pm.expect(tv4.validate(jsonData, schema)).to.be.true;", - "});", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", - " pm.test(\"Validate Schema for \" + testTitle, function(){", - " pm.expect(tv4.validate(element, schema)).to.be.true;", - " });", - " ", - " // Test the authenticate response.", - " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", - " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", - " });", - " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", - " pm.expect(element.actual_service_ind).to.be.eql(1);", - " });", - " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the service IDs.", - "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Get Exam Types", - "item": [ - { - "name": "Exam Type List", - "event": [ - { - "listen": "test", - "script": { - "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", - "exec": [ - "// Define the JSON Schema expected in response", - "var examTypeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_color\": {\"type\": \"string\"},", - " \"exam_type_id\": {\"type\": \"number\"},", - " \"exam_type_name\": {\"type\": \"string\"},", - " \"ita_ind\": {\"type\": \"number\"},", - " \"method_type\": {\"type\": \"string\"},", - " \"number_of_hours\": {\"type\": \"number\"},", - " \"group_exam_ind\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", - " },", - " \"required\": []", - "}; ", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Exam Type Schema\", function(){", - " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", - "});", - "", - "// Store all exam type IDs for future use in adding exams", - "var allExamIds = [];", - "", - "// Make sure some data returned.", - "pm.test(\"Response has exam_types property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", - "});", - "pm.test(\"Response has at least one exam_type\", function(){", - " pm.expect(jsonData.exam_types.length).to.be.above(0);", - "});", - "", - "// Set up list of valid exam types, create random functions.", - "eval(environment.init_exam_type_data);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exam_types/", - "host": [ - "{{url}}exam_types" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exams", - "item": [ - { - "name": "Exam Post End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", - "", - "// Store other variables for later use.", - "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", - "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", - "", - "// Calculate an expiry date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format expiry date for the exam.", - "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "", - "// If jsonData has an exam property, save exam ID.", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_list_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Put End-point", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "3efcb554-1e5a-4e0a-91b8-aa32eb14a5e6", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.exam_schema_check);", - "eval(environment.exam_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Exam Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", - "exec": [ - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exams Export", - "item": [ - { - "name": "Exams Export List", - "event": [ - { - "listen": "test", - "script": { - "id": "9e02fbaf-627e-43db-b516-226b1a2874e8", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than 20,000ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(20000);", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "caf977c6-da22-4f6c-b0c9-ca5f0fc3ec29", - "exec": [ - "// Calculate a start date four weeks prior to today.", - "var start = new Date();", - "start.setDate(start.getDate()-28);", - "", - "// Get year, day, month from the start time.", - "start_year = start.getFullYear().toString();", - "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", - "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", - "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", - "", - "// Get year, day, month from the current day.", - "var today = new Date();", - "end_year = today.getFullYear().toString();", - "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", - "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", - "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", - "", - "console.log(\"Start: \" + start_date);", - "console.log(\"End: \" + end_date);", - "pm.globals.set(\"start_date\", start_date);", - "pm.globals.set(\"end_date\", end_date);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "export", - "" - ], - "query": [ - { - "key": "start_date", - "value": "{{start_date}}" - }, - { - "key": "end_date", - "value": "{{end_date}}" - } - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam Types", - "item": [ - { - "name": "Exam Type List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", - "exec": [ - "// Define the JSON Schema expected in response", - "var examTypeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_color\": {\"type\": \"string\"},", - " \"exam_type_id\": {\"type\": \"number\"},", - " \"exam_type_name\": {\"type\": \"string\"},", - " \"ita_ind\": {\"type\": \"number\"},", - " \"method_type\": {\"type\": \"string\"},", - " \"number_of_hours\": {\"type\": \"number\"},", - " \"group_exam_ind\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", - " },", - " \"required\": []", - "}; ", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Exam Type Schema\", function(){", - " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exam_types/", - "host": [ - "{{url}}exam_types" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Bookings", - "item": [ - { - "name": "Booking Post End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "a4099862-1c9a-48f7-adbf-0240e6eb185a", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "", - "var booking_id = jsonData.booking.booking_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "4df5dedd-4976-4cdc-b2be-739b2028cdc4", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "// Store globals.", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" - }, - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "232f5232-de75-4f55-b999-2e39e39a12a8", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "59fc7e18-6df0-48d9-822c-6518fbece309", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Put End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "b127046f-b399-4e66-b76f-b6e79268b8ce", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9bde67e2-8eeb-4e50-90bf-caff1fef90a4", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", - "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", - "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Detail End-point Again", - "event": [ - { - "listen": "test", - "script": { - "id": "b5e53be0-ddda-4c99-b563-5a3b449e0f79", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.booking_schema_check);", - "eval(environment.booking_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Booking Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1e7c1d71-ea74-4a78-9ef2-ee9a7a47d915", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}bookings/{{booking_id}}/", - "host": [ - "{{url}}bookings" - ], - "path": [ - "{{booking_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Invigilators", - "item": [ - { - "name": "Invigilator List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "0e68858e-985c-4fb0-bca6-04d33f65c28d", - "exec": [ - "// Define the JSON Schema expected in response", - "var invigilatorSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"invigilators\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"contact_email\": {\"type\": \"string\"},", - " \"contact_phone\": {\"type\": \"string\"},", - " \"contract_expiry_date\": {\"type\": \"string\"},", - " \"contract_number\": {\"type\": \"string\"},", - " \"invigilator_id\": {\"type\": \"number\"},", - " \"invigilator_name\": {\"type\": \"string\"},", - " \"invigilator_notes\": {\"type\": \"string\"},", - " \"office\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\", \"office\"]", - " }", - " }", - "};", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Invigilator Schema\", function(){", - " pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;", - "});", - "", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}invigilators/", - "host": [ - "{{url}}invigilators" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Rooms", - "item": [ - { - "name": "Room List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", - "exec": [ - "// Define the JSON Schema expected in response", - "var roomSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"rooms\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"capacity\": {\"type\": \"number\"},", - " \"color\": {\"type\": \"string\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"} ", - " },", - " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " },", - " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", - " }", - " }", - "};", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Room Schema\", function(){", - " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}rooms/", - "host": [ - "{{url}}rooms" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "CSRS", - "item": [ - { - "name": "CSRS Me Get End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1ed052a0-06cc-45c7-ae49-b19b96e02d51", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"back_office_list\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"actual_service_ind\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"display_dashboard_ind\": {\"type\": \"number\"},", - " \"parent\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"service_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"service_name\"]", - " },", - " \"parent_id\": {\"type\": \"number\"},", - " \"prefix\": {\"type\": \"string\"},", - " \"service_code\": {\"type\": \"string\"},", - " \"service_desc\": {\"type\": \"string\"},", - " \"service_id\": {\"type\": \"number\"},", - " \"service_name\": {\"type\": \"string\"},", - " },", - " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", - " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", - " \"service_name\"]", - " },", - " \"counters\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"counter_id\": {\"type\": \"number\"},", - " \"counter_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"counter_id\", \"counter_name\"]", - " },", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"quick_list\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"actual_service_ind\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"display_dashboard_ind\": {\"type\": \"number\"},", - " \"parent\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"service_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"service_name\"]", - " },", - " \"parent_id\": {\"type\": \"number\"},", - " \"prefix\": {\"type\": \"string\"},", - " \"service_code\": {\"type\": \"string\"},", - " \"service_desc\": {\"type\": \"string\"},", - " \"service_id\": {\"type\": \"number\"},", - " \"service_name\": {\"type\": \"string\"},", - " },", - " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", - " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", - " \"service_name\"]", - " },", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"},", - " \"timezone\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"timezone_id\": {\"type\": \"number\"},", - " \"timezone_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"timezone_id\", \"timezone_name\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"back_office_list\", \"counters\",", - " \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\",", - " \"quick_list\", \"sb\", \"sb_id\", ]", - " },", - " \"office_id\": {\"type\": \"number\"},", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " },", - " \"attention_needed\": {\"type\": \"boolean\"},", - " \"active_citizens\" : {\"type\": \"array\"},", - " \"back_office_display\": {\"type\": \"string\"},", - " \"recurring_feature_flag\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr\", \"attention_needed\", \"active_citizens\", \"back_office_display\", \"recurring_feature_flag\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "1c4020cd-fb05-478c-b908-3197c80d492e", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Offices", - "item": [ - { - "name": "Office List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "ad6ffb6d-efad-4e11-b4a9-99e49f457bbd", - "exec": [ - "// Define the JSON Schema expected in response", - "var officeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"offices\": {", - " \"type\": \"array\",", - " \"properties\": {", - " \"exams_enabled\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " },", - " \"sb_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exams_enabled\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", - " }", - " }", - "};", - "", - "// Get the max response time allowed.", - "var response_max = JSON.parse(globals.response_max);", - "", - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response Office Schema\", function(){", - " pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}offices/", - "host": [ - "{{url}}offices" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Appointments", - "item": [ - { - "name": "Appointment Post End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "851b530c-92d7-482c-9e80-2a1f542b280c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 201\", function(){", - " pm.response.to.have.status(201);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "", - "var appointment_id = jsonData.appointment.appointment_id;", - "", - "//Dynamic variable used for end-point testing later on", - "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e8444129-3a7b-4a2f-82d0-fd9b5d395f0d", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", - "pm.globals.set(\"comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", - "pm.globals.set(\"citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", - "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}},\n \"citizen_name\": {{citizen_name}}\n}" - }, - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Detail End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment List End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid\\", - "eval(environment.appointment_list_schema_check);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}appointments/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Put End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "1cf958af-12dd-44f4-a624-d070cc6d90cb", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "eval(environment.appointment_schema_check);", - "eval(environment.appointment_data_check);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "52ba16f8-f08e-4a75-a64c-39e59c64c1c6", - "exec": [ - "// Calculate a start date a week from today.", - "var later = new Date();", - "later.setDate(later.getDate()+7);", - "", - "// Get year, day, month from the later time.", - "year = later.getFullYear().toString();", - "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", - "day = (\"0\" + (later.getDate()).toString()).slice(-2);", - "", - "// Format starting and ending time for the booking.", - "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", - "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", - "", - "pm.globals.set(\"start_time\", JSON.stringify(start_time));", - "pm.globals.set(\"end_time\", JSON.stringify(end_time));", - "pm.globals.set(\"comments\", JSON.stringify(\"super EARLY\"));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}}\n}" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Appointment Delete End-point", - "event": [ - { - "listen": "test", - "script": { - "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", - "exec": [ - "// Check Response code for request", - "pm.test(\"Response code for request is 204\", function(){", - " pm.response.to.have.status(204);", - "});", - "", - "// Check if response time less than max allowed.", - "var response_max = JSON.parse(globals.response_max);", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}appointments/{{appointment_id}}/", - "host": [ - "{{url}}appointments" - ], - "path": [ - "{{appointment_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "id": "1bfedebf-df09-4b2b-8c49-78fc3b07e34b", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "787fad5e-5387-4a35-a8f5-0fdb115be54a", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "variable": [ - { - "id": "278ecdd2-62d5-4df1-8d7f-b4cc71138575", - "key": "auth_variable", - "value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1NXhiZzcyaml6NWQ3cEtkbzdwNFhLdTB2VFA2UXMtemthZzFaY1ZKWTZVIn0.eyJqdGkiOiJmZWVlYzU4YS0zOTY0LTQwZDgtOTk3Ni02OTg1ZjNiNmQ2NzYiLCJleHAiOjE1NDQwMzY2NzQsIm5iZiI6MCwiaWF0IjoxNTQ0MDM0ODc0LCJpc3MiOiJodHRwczovL3Nzby10ZXN0LnBhdGhmaW5kZXIuZ292LmJjLmNhL2F1dGgvcmVhbG1zL3NiYyIsImF1ZCI6ImNmbXMtREVWIiwic3ViIjoiY2ZmNTQ3MDItZTkzMC00YjZlLTlhYzgtMmZkMTRjY2FmNzUxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY2Ztcy1ERVYiLCJub25jZSI6ImM1Zjg1NzBmLTgwNDktNDUxNC04Mjg4LTJjN2RmMzdkZTc5NSIsImF1dGhfdGltZSI6MTU0NDAzMjgxNywic2Vzc2lvbl9zdGF0ZSI6IjM4YjhiYzY4LTdmZDktNDE0NC04YWQ1LTI4ZGRlNmMwYWE1MCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiQ0ZNUyBQb3N0bWFuIE9wZXJhdG9yIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiY2Ztcy1wb3N0bWFuLW9wZXJhdG9yIiwiZ2l2ZW5fbmFtZSI6IkNGTVMgUG9zdG1hbiIsImZhbWlseV9uYW1lIjoiT3BlcmF0b3IifQ.CaDq7kLDMu1oqUVm6fhiAw4ubu_D4jVdkXrqLjfCxu9b-9GzyNSIK1Gy0NCesN3caG684-Ofv3LlragMOwdoRhx-xkO6j5rqPhh2EdEDYTGkrnmQrVBjgHECLYN3RSAUWk4oKsp7gRMLDorU6QWLZNRWe9qlzulbss08Mut4nZz1RV0CirvJQ9oshBdpstQIPBM7ZEptcF2AOnc8swcFMJ3mqdrs0ImytArwZEzYwRzSIAR0-kYW-AXq-TK1dk2R7fqkMXmy3ZdHoQlFRfNtfYILVPzBAq3zPftrD-lmCcfUWz-0UcwhmNELQ-Pej-t_Z_eFSPPGkAVIuv_kkHbqQw", - "type": "string" - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file + "info": { + "_postman_id": "a0066296-ea47-4c6a-834a-ec58a02f8dea", + "name": "Load_Test_Booking", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Setup", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "id": "5409b66d-67a4-449b-8633-9aeca632b388", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Authentication Token", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "454ea7d6-4c7e-4571-8bc1-be7eed57d03a", + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + } + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Type-Data", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"init_exam_type_data\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Get-Random", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_random_functions\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"booking\": {\n \"type\": \"object\",\n \"properties\": {\n \"blackout_flag\": {\"type\": \"string\"},\n \"blackout_notes\": {\"type\": [\"string\", \"null\"]},\n \"booking_contact_information\": {\"type\": [\"string\", \"null\"]},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": [\"string\", \"null\"]},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilators\": {\"type\": \"array\"},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"timezone\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"recurring_uuid\": {\"type\": [\"string\", \"null\"]},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"deleted\": {\"type\": [\"string\", \"null\"]},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\", \"deleted\"]\n },\n \n \"room_id\": {\"type\": \"number\"},\n \"sbc_staff_invigilated\": {\"type\": \"number\"},\n \"shadow_invigilator_id\": {\"type\": [\"number\", \"null\"]},\n \"start_time\": {\"type\": \"string\"}\n },\n \"required\": [\"blackout_flag\", \"blackout_notes\", \"booking_contact_information\", \"booking_id\",\n \"booking_name\", \"end_time\", \"fees\", \"invigilators\", \"office\", \"office_id\",\n \"recurring_uuid\", \"room\", \"room_id\", \"sbc_staff_invigilated\", \"shadow_invigilator_id\",\n \"start_time\"]\n },\n \"errors\": { \"type\": \"object\" }\n },\n \"required\": [\"booking\", \"errors\"]\n}\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an booking property.\npm.test(\"Response should have booking property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"booking\")).to.be.true;\n});\n\n// If jsonData has booking property, check data.\nif (jsonData.hasOwnProperty(\"booking\")) {\n\n //Test to see if booking name has expected value\n pm.test(\"Validate Booking Name has expected value\", function(){\n pm.expect(jsonData.booking_name === environment.booking_name);\n });\n\n //Test to see if start time has expected value\n pm.test(\"Validate start time has expected value\", function(){\n pm.expect(jsonData.start_time === environment.start_time);\n });\n\n //Test to see if end time has expected value\n pm.test(\"Validate end time has expected value\", function(){\n pm.expect(jsonData.end_time === environment.end_time);\n });\n\n //Test to see if room id has expected value\n pm.test(\"Validate room id has expected value\", function(){\n pm.expect(jsonData.room_id === environment.room_id_1);\n });\n\n //Test to see if fees field has expected value\n pm.test(\"Validate fees has expected value\", function(){\n pm.expect(jsonData.fees === environment.fees);\n });\n\n //Test to see if office id field has expected value\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Booking-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"booking_list_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var bookingSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"bookings\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_contact_information\": {},\n \"booking_id\": {\"type\": \"number\"},\n \"booking_name\": {\"type\": \"string\"},\n \"end_time\": {\"type\": \"string\"},\n \"fees\": {\"type\": \"string\"},\n \"invigilator\": {},\n \"invigilator_id\": {},\n \"office\": {\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\"},\n \"sb_id\": {\"type\": \"number\"},\n \"timezone\": {}\n },\n \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]\n },\n \"office_id\": {\"type\": \"number\"},\n \"room\": {\n \"type\": \"object\",\n \"properties\": {\n \"capacity\": {\"type\": \"number\"},\n \"color\": {\"type\": \"string\"},\n \"room_id\": {\"type\": \"number\"},\n \"room_name\": {\"type\": \"string\"}\n },\n \"required\": [\"capacity\", \"color\", \"room_id\", \"room_name\"]\n },\n \"room_id\": {\"type\": \"number\"},\n \"start_time\": {\"type\": \"string\"},\n },\n \"required\": [\"booking_contact_information\", \"booking_id\", \"booking_name\", \"end_time\", \"fees\", \"room\", \"room_id\", \"start_time\", \"invigilator_id\", \"office\", \"office_id\"]\n },\n \"errors\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n \"required\": [\"bookings\", \"errors\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Booking List Schema\", function(){\n pm.expect(tv4.validate(jsonData, bookingSchema)).to.be.true;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Room IDs", + "event": [ + { + "listen": "test", + "script": { + "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", + "exec": [ + "// Define the JSON Schema expected in response", + "var roomSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"rooms\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"capacity\": {\"type\": \"number\"},", + " \"color\": {\"type\": \"string\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"} ", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", + " }", + " }", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Room Schema\", function(){", + " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", + "});", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"rooms\")) {", + " console.log(\"==> Rooms\");", + " room_id_1 = jsonData.rooms[0].room_id;", + " room_id_2 = room_id_1;", + " if (jsonData.rooms.length > 1) {", + " room_id_2 = jsonData.rooms[1].room_id;", + " }", + " postman.setEnvironmentVariable(\"room_id_1\", JSON.stringify(room_id_1.toString()));", + " postman.setEnvironmentVariable(\"room_id_2\", JSON.stringify(room_id_2.toString()));", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "462566f5-d6cd-4c14-9b11-0cbf7abc0caf", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Service IDs", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3b11031e-4031-4569-91f2-49e23517ffee", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"services\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"number\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + " }", + " }", + "};", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Service Schema\", function(){", + " pm.expect(tv4.validate(jsonData, schema)).to.be.true;", + "});", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name;", + " pm.test(\"Validate Schema for \" + testTitle, function(){", + " pm.expect(tv4.validate(element, schema)).to.be.true;", + " });", + " ", + " // Test the authenticate response.", + " pm.test(\"--> \" + testTitle + \" dashboard_ind must be 0 or 1\", function() {", + " pm.expect(element.display_dashboard_ind).to.be.within(0,1);", + " });", + " pm.test(\"--> \" + testTitle + \" actual_service_ind must be 1\", function() {", + " pm.expect(element.actual_service_ind).to.be.eql(1);", + " });", + " pm.test(\"--> \" + testTitle + \" parent_id must not be null\", function() {", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the service IDs.", + "pm.test(\"The \" + mspText + \" service ID (\" + mspId.toString() + \") should not equal 0\", function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "pm.test(\"The \" + propTaxText + \" service ID (\" + taxId.toString() + \") should not equal 0\", function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Get Exam Types", + "item": [ + { + "name": "Exam Type List", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", + "exec": [ + "// Define the JSON Schema expected in response", + "var examTypeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_color\": {\"type\": \"string\"},", + " \"exam_type_id\": {\"type\": \"number\"},", + " \"exam_type_name\": {\"type\": \"string\"},", + " \"ita_ind\": {\"type\": \"number\"},", + " \"method_type\": {\"type\": \"string\"},", + " \"number_of_hours\": {\"type\": \"number\"},", + " \"group_exam_ind\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", + " },", + " \"required\": []", + "}; ", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Exam Type Schema\", function(){", + " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", + "});", + "", + "// Store all exam type IDs for future use in adding exams", + "var allExamIds = [];", + "", + "// Make sure some data returned.", + "pm.test(\"Response has exam_types property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", + "});", + "pm.test(\"Response has at least one exam_type\", function(){", + " pm.expect(jsonData.exam_types.length).to.be.above(0);", + "});", + "", + "// Set up list of valid exam types, create random functions.", + "eval(environment.init_exam_type_data);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exam_types/", + "host": [ + "{{url}}exam_types" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exams", + "item": [ + { + "name": "Exam Post End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\",\"expiry_date\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));", + "// Store other variables for later use.", + "postman.setEnvironmentVariable(\"exam_method\", JSON.stringify(\"paper\"));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"Postman Group Exam Name\"));", + "postman.setEnvironmentVariable(\"exam_written_ind\", JSON.stringify(\"0\"));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"Postman Examinee Name\"));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"Postman Test Notes\"));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"19\"));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"Postman test location\"));", + "// Calculate an expiry date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format expiry date for the exam.", + "expiry_date = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"expiry_date\", JSON.stringify(expiry_date));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "", + "// If jsonData has an exam property, save exam ID.", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\",\n \"expiry_date\": {{expiry_date}}\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_list_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Put End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_method\",\"exam_name\",\"random_exam_type_id\",\"exam_written_ind\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"number_of_students\", JSON.stringify(\"21\"));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : {{exam_method}},\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : {{exam_written_ind}},\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3efcb554-1e5a-4e0a-91b8-aa32eb14a5e6", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.exam_schema_check);", + "eval(environment.exam_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Exam Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", + "exec": [ + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exams Export", + "item": [ + { + "name": "Exams Export List", + "event": [ + { + "listen": "test", + "script": { + "id": "9e02fbaf-627e-43db-b516-226b1a2874e8", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than 20,000ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(20000);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "caf977c6-da22-4f6c-b0c9-ca5f0fc3ec29", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"start_date\",\"end_date\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date four weeks prior to today.", + "var start = new Date();", + "start.setDate(start.getDate()-28);", + "// Get year, day, month from the start time.", + "start_year = start.getFullYear().toString();", + "start_month = (\"0\" + (start.getMonth() + 1).toString()).slice(-2);", + "start_day = (\"0\" + (start.getDate()).toString()).slice(-2);", + "start_date = start_year + \"-\" + start_month + \"-\" + start_day;", + "// Get year, day, month from the current day.", + "var today = new Date();", + "end_year = today.getFullYear().toString();", + "end_month = (\"0\" + (today.getMonth() + 1).toString()).slice(-2);", + "end_day = (\"0\" + (today.getDate()).toString()).slice(-2);", + "end_date = end_year + \"-\" + end_month + \"-\" + end_day;", + "console.log(\"Start: \" + start_date);", + "console.log(\"End: \" + end_date);", + "pm.globals.set(\"start_date\", start_date);", + "pm.globals.set(\"end_date\", end_date);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exams/export/?start_date={{start_date}}&end_date={{end_date}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "export", + "" + ], + "query": [ + { + "key": "start_date", + "value": "{{start_date}}" + }, + { + "key": "end_date", + "value": "{{end_date}}" + } + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam Types", + "item": [ + { + "name": "Exam Type List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", + "exec": [ + "// Define the JSON Schema expected in response", + "var examTypeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_color\": {\"type\": \"string\"},", + " \"exam_type_id\": {\"type\": \"number\"},", + " \"exam_type_name\": {\"type\": \"string\"},", + " \"ita_ind\": {\"type\": \"number\"},", + " \"method_type\": {\"type\": \"string\"},", + " \"number_of_hours\": {\"type\": \"number\"},", + " \"group_exam_ind\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", + " },", + " \"required\": []", + "}; ", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Exam Type Schema\", function(){", + " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exam_types/", + "host": [ + "{{url}}exam_types" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Bookings", + "item": [ + { + "name": "Booking Post End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "a4099862-1c9a-48f7-adbf-0240e6eb185a", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "", + "var booking_id = jsonData.booking.booking_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"booking_id\", JSON.stringify(booking_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "4df5dedd-4976-4cdc-b2be-739b2028cdc4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"start_time\",\"end_time\",\"room_id\",\"fees\",\"booking_name\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "// Store globals.", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"false\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Super big demo next week\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_1\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}\n" + }, + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "232f5232-de75-4f55-b999-2e39e39a12a8", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "59fc7e18-6df0-48d9-822c-6518fbece309", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Put End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "b127046f-b399-4e66-b76f-b6e79268b8ce", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9bde67e2-8eeb-4e50-90bf-caff1fef90a4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\",\"start_time\",\"end_time\",\"room_id\",\"fees\",\"booking_name\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"fees\", JSON.stringify(\"true\"));", + "pm.globals.set(\"booking_name\", JSON.stringify(\"Time to pay your fees!\"));", + "pm.globals.set(\"room_id\", pm.environment.get(\"room_id_2\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"room_id\": {{room_id}},\n \"fees\": {{fees}},\n \"booking_name\": {{booking_name}},\n \"office_id\" : {{current_office_id}}\n}" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Detail End-point Again", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "b5e53be0-ddda-4c99-b563-5a3b449e0f79", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.booking_schema_check);", + "eval(environment.booking_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Booking Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"booking_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1e7c1d71-ea74-4a78-9ef2-ee9a7a47d915", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}bookings/{{booking_id}}/", + "host": [ + "{{url}}bookings" + ], + "path": [ + "{{booking_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Invigilators", + "item": [ + { + "name": "Invigilator List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "0e68858e-985c-4fb0-bca6-04d33f65c28d", + "exec": [ + "// Define the JSON Schema expected in response", + "var invigilatorSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"invigilators\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"contact_email\": {\"type\": \"string\"},", + " \"contact_phone\": {\"type\": \"string\"},", + " \"contract_expiry_date\": {\"type\": \"string\"},", + " \"contract_number\": {\"type\": \"string\"},", + " \"invigilator_id\": {\"type\": \"number\"},", + " \"invigilator_name\": {\"type\": \"string\"},", + " \"invigilator_notes\": {\"type\": \"string\"},", + " \"office\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"contact_email\", \"contact_phone\", \"contract_expiry_date\", \"contract_number\", \"invigilator_id\", \"invigilator_name\", \"invigilator_notes\", \"office\"]", + " }", + " }", + "};", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Invigilator Schema\", function(){", + " pm.expect(tv4.validate(jsonData, invigilatorSchema)).to.be.true;", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}invigilators/", + "host": [ + "{{url}}invigilators" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Rooms", + "item": [ + { + "name": "Room List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "859ae114-22c4-4f3e-9f1f-9f0e06fbc29d", + "exec": [ + "// Define the JSON Schema expected in response", + "var roomSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"rooms\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"capacity\": {\"type\": \"number\"},", + " \"color\": {\"type\": \"string\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"} ", + " },", + " \"required\": [\"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " },", + " \"required\": [\"capacity\", \"color\", \"office\", \"room_id\", \"room_name\"]", + " }", + " }", + "};", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Room Schema\", function(){", + " pm.expect(tv4.validate(jsonData, roomSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}rooms/", + "host": [ + "{{url}}rooms" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "CSRS", + "item": [ + { + "name": "CSRS Me Get End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "1ed052a0-06cc-45c7-ae49-b19b96e02d51", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"back_office_list\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"actual_service_ind\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"display_dashboard_ind\": {\"type\": \"number\"},", + " \"parent\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"service_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"service_name\"]", + " },", + " \"parent_id\": {\"type\": \"number\"},", + " \"prefix\": {\"type\": \"string\"},", + " \"service_code\": {\"type\": \"string\"},", + " \"service_desc\": {\"type\": \"string\"},", + " \"service_id\": {\"type\": \"number\"},", + " \"service_name\": {\"type\": \"string\"},", + " },", + " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", + " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", + " \"service_name\"]", + " },", + " \"counters\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"counter_id\": {\"type\": \"number\"},", + " \"counter_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"counter_id\", \"counter_name\"]", + " },", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"quick_list\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"actual_service_ind\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"display_dashboard_ind\": {\"type\": \"number\"},", + " \"parent\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"service_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"service_name\"]", + " },", + " \"parent_id\": {\"type\": \"number\"},", + " \"prefix\": {\"type\": \"string\"},", + " \"service_code\": {\"type\": \"string\"},", + " \"service_desc\": {\"type\": \"string\"},", + " \"service_id\": {\"type\": \"number\"},", + " \"service_name\": {\"type\": \"string\"},", + " },", + " \"required\": [\"actual_service_ind\", \"deleted\", \"display_dashboard_ind\", \"parent\",", + " \"parent_id\", \"prefix\", \"service_code\", \"service_desc\", \"service_id\",", + " \"service_name\"]", + " },", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"},", + " \"timezone\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"timezone_id\": {\"type\": \"number\"},", + " \"timezone_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"timezone_id\", \"timezone_name\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"back_office_list\", \"counters\",", + " \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\",", + " \"quick_list\", \"sb\", \"sb_id\", ]", + " },", + " \"office_id\": {\"type\": \"number\"},", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " },", + " \"attention_needed\": {\"type\": \"boolean\"},", + " \"active_citizens\" : {\"type\": \"array\"},", + " \"back_office_display\": {\"type\": \"string\"},", + " \"recurring_feature_flag\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr\", \"attention_needed\", \"active_citizens\", \"back_office_display\", \"recurring_feature_flag\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "1c4020cd-fb05-478c-b908-3197c80d492e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Offices", + "item": [ + { + "name": "Office List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ad6ffb6d-efad-4e11-b4a9-99e49f457bbd", + "exec": [ + "// Define the JSON Schema expected in response", + "var officeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"offices\": {", + " \"type\": \"array\",", + " \"properties\": {", + " \"exams_enabled\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " },", + " \"sb_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exams_enabled\", \"office_id\", \"office_name\", \"office_number\", \"sb\", \"sb_id\"]", + " }", + " }", + "};", + "", + "// Get the max response time allowed.", + "var response_max = JSON.parse(globals.response_max);", + "", + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response Office Schema\", function(){", + " pm.expect(tv4.validate(jsonData, officeSchema)).to.be.true;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}offices/", + "host": [ + "{{url}}offices" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Appointments", + "item": [ + { + "name": "Appointment Post End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "851b530c-92d7-482c-9e80-2a1f542b280c", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 201\", function(){", + " pm.response.to.have.status(201);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "", + "var appointment_id = jsonData.appointment.appointment_id;", + "", + "//Dynamic variable used for end-point testing later on", + "postman.setEnvironmentVariable(\"appointment_id\", JSON.stringify(appointment_id));" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e8444129-3a7b-4a2f-82d0-fd9b5d395f0d", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"service_id\",\"current_office_id\",\"start_time\",\"end_time\",\"category\",\"comments\",\"citizen_name\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T17:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T19:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"category\", JSON.stringify(\"Exam\"));", + "pm.globals.set(\"comments\", JSON.stringify(\"Missed playoffs, needs to talk #1.\"));", + "pm.globals.set(\"citizen_name\", JSON.stringify(\"LeBron James Appointment #1\"));", + "pm.globals.set(\"service_id\", pm.environment.get(\"service_PropTax_id\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"service_id\": {{service_id}},\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}},\n \"citizen_name\": {{citizen_name}}\n}" + }, + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Detail End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "5206f620-8220-4ba4-860a-13c1bb452e6d", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment List End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a7432b9-ffb0-4e23-a224-c3a48171d6c4", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid\\", + "eval(environment.appointment_list_schema_check);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}appointments/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Put End-point", + "event": [ + { + "listen": "test", + "script": { + "id": "1cf958af-12dd-44f4-a624-d070cc6d90cb", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "eval(environment.appointment_schema_check);", + "eval(environment.appointment_data_check);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "52ba16f8-f08e-4a75-a64c-39e59c64c1c6", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\",\"current_office_id\",\"start_time\",\"end_time\",\"category\",\"comments\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Calculate a start date a week from today.", + "var later = new Date();", + "later.setDate(later.getDate()+7);", + "// Get year, day, month from the later time.", + "year = later.getFullYear().toString();", + "month = (\"0\" + (later.getMonth() + 1).toString()).slice(-2);", + "day = (\"0\" + (later.getDate()).toString()).slice(-2);", + "// Format starting and ending time for the booking.", + "start_time = year + \"-\" + month + \"-\" + day + \"T21:00:00Z\";", + "end_time = year + \"-\" + month + \"-\" + day + \"T23:00:00Z\";", + "pm.globals.set(\"start_time\", JSON.stringify(start_time));", + "pm.globals.set(\"end_time\", JSON.stringify(end_time));", + "pm.globals.set(\"comments\", JSON.stringify(\"super EARLY\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"office_id\" : {{current_office_id}},\n \"start_time\": {{start_time}},\n \"end_time\": {{end_time}},\n \"category\": {{category}},\n \"comments\": {{comments}}\n}" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Appointment Delete End-point", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"appointment_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "88b43898-330f-4cf1-81c3-8cce3c53f54c", + "exec": [ + "// Check Response code for request", + "pm.test(\"Response code for request is 204\", function(){", + " pm.response.to.have.status(204);", + "});", + "", + "// Check if response time less than max allowed.", + "var response_max = JSON.parse(globals.response_max);", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}appointments/{{appointment_id}}/", + "host": [ + "{{url}}appointments" + ], + "path": [ + "{{appointment_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "1bfedebf-df09-4b2b-8c49-78fc3b07e34b", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "787fad5e-5387-4a35-a8f5-0fdb115be54a", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "id": "278ecdd2-62d5-4df1-8d7f-b4cc71138575", + "key": "auth_variable", + "value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1NXhiZzcyaml6NWQ3cEtkbzdwNFhLdTB2VFA2UXMtemthZzFaY1ZKWTZVIn0.eyJqdGkiOiJmZWVlYzU4YS0zOTY0LTQwZDgtOTk3Ni02OTg1ZjNiNmQ2NzYiLCJleHAiOjE1NDQwMzY2NzQsIm5iZiI6MCwiaWF0IjoxNTQ0MDM0ODc0LCJpc3MiOiJodHRwczovL3Nzby10ZXN0LnBhdGhmaW5kZXIuZ292LmJjLmNhL2F1dGgvcmVhbG1zL3NiYyIsImF1ZCI6ImNmbXMtREVWIiwic3ViIjoiY2ZmNTQ3MDItZTkzMC00YjZlLTlhYzgtMmZkMTRjY2FmNzUxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY2Ztcy1ERVYiLCJub25jZSI6ImM1Zjg1NzBmLTgwNDktNDUxNC04Mjg4LTJjN2RmMzdkZTc5NSIsImF1dGhfdGltZSI6MTU0NDAzMjgxNywic2Vzc2lvbl9zdGF0ZSI6IjM4YjhiYzY4LTdmZDktNDE0NC04YWQ1LTI4ZGRlNmMwYWE1MCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiQ0ZNUyBQb3N0bWFuIE9wZXJhdG9yIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiY2Ztcy1wb3N0bWFuLW9wZXJhdG9yIiwiZ2l2ZW5fbmFtZSI6IkNGTVMgUG9zdG1hbiIsImZhbWlseV9uYW1lIjoiT3BlcmF0b3IifQ.CaDq7kLDMu1oqUVm6fhiAw4ubu_D4jVdkXrqLjfCxu9b-9GzyNSIK1Gy0NCesN3caG684-Ofv3LlragMOwdoRhx-xkO6j5rqPhh2EdEDYTGkrnmQrVBjgHECLYN3RSAUWk4oKsp7gRMLDorU6QWLZNRWe9qlzulbss08Mut4nZz1RV0CirvJQ9oshBdpstQIPBM7ZEptcF2AOnc8swcFMJ3mqdrs0ImytArwZEzYwRzSIAR0-kYW-AXq-TK1dk2R7fqkMXmy3ZdHoQlFRfNtfYILVPzBAq3zPftrD-lmCcfUWz-0UcwhmNELQ-Pej-t_Z_eFSPPGkAVIuv_kkHbqQw", + "type": "string" + } + ], + "protocolProfileBehavior": {} +} diff --git a/api/postman/Load_Test_Exam.json b/api/postman/Load_Test_Exam.json index ba772d631..e49d4901f 100644 --- a/api/postman/Load_Test_Exam.json +++ b/api/postman/Load_Test_Exam.json @@ -1,3497 +1,3859 @@ { - "info": { - "_postman_id": "b8ea4228-164b-4447-a63e-d2c384530553", - "name": "Load_Test_Exam", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Setup", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.environment.set(\"token\", jsonData.access_token);\n\t pm.environment.set(\"refresh_token\", jsonData.refresh_token);\n\t console.log(err ? err : res.json());\n\t} else {\n\t pm.environment.set(\"token\", 0);\n\t pm.environment.set(\"refresh_token\", 0);\n\t pm.environment.set(\"token_expires\", 0);\n\t pm.environment.set(\"refresh_token_expires\", 0);\n\t}\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-Script", - "event": [ - { - "listen": "test", - "script": { - "id": "f386cf22-bb0f-47d9-9c22-cda162ed4375", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = environment.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Test the authenticate response.\npm.test(\"Response time should be below 1500ms\", function() {\n pm.expect(pm.response.responseTime).to.be.below(1500);\n});\npm.test('Response statusCode should be 200 OK', function() {\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Complex-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "f006b951-3205-4f21-a315-369f85591929", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"complex_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Test the authenticate response.\npm.test('Response time should be below 25000ms', function() {\n pm.response.responseTime.to.be.below(25000);\n});\npm.test('Response statusCode should be 200 OK', function() {\n response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Create-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Test the authenticate response.\npm.test('Response time should be below 20000ms', function() {\n pm.expect(pm.response.responseTime).to.be.below(20000);\n});\npm.test('Response statusCode should be 201 CREATED', function() {\n pm.response.to.have.status(201);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Type-Data", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"init_exam_type_data\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Get-Random", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_random_functions\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-Data-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_data_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Exam-List-Schema-Check", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "Who am I", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ], - "description": "This folder performs basic authentication features.", - "protocolProfileBehavior": {} - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Set health response time variable.", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum load time allowed.", - "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints", - "protocolProfileBehavior": {} - }, - { - "name": "Check user login", - "item": [ - { - "name": "Authenticate user", - "event": [ - { - "listen": "test", - "script": { - "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", - "exec": [ - "// Do the basic checks.", - "eval(environment.basic_response_test);", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " pm.expect(access_token).not.eql(null);", - "});", - "", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " pm.expect(refresh_token).not.eql(null);", - "});", - "", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " pm.expect(expires_in).not.eql(null);", - "});", - "", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " pm.expect(refresh_expires_in).not.eql(null);", - "});", - "", - "var jsonData = pm.response.json();", - "pm.globals.set(\"token\", jsonData.access_token);", - "pm.globals.set(\"refresh_token\", jsonData.refresh_token);", - "pm.globals.set(\"token_expires\", Date.now()+(jsonData.expires_in * 1000));", - "pm.globals.set(\"refresh_token_expires\", Date.now()+(jsonData.refresh_expires_in * 1000));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "description": "Make sure the operator ID can log in" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Get Exam Types", - "item": [ - { - "name": "Exam Type List", - "event": [ - { - "listen": "test", - "script": { - "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", - "exec": [ - "// Define the JSON Schema expected in response", - "var examTypeSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"exam_color\": {\"type\": \"string\"},", - " \"exam_type_id\": {\"type\": \"number\"},", - " \"exam_type_name\": {\"type\": \"string\"},", - " \"ita_ind\": {\"type\": \"number\"},", - " \"method_type\": {\"type\": \"string\"},", - " \"number_of_hours\": {\"type\": \"number\"},", - " \"group_exam_ind\": {\"type\": \"number\"}", - " },", - " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", - " },", - " \"required\": []", - "}; ", - "", - "// Get the max response time allowed.", - "console.log(\"==> Before getting response max\");", - "var response_max = JSON.parse(globals.max_response_time);", - "", - "// Check Response code for request", - "console.log(\"==> Before checking response code\");", - "pm.test(\"Response code for request is 200\", function(){", - " pm.response.to.have.status(200);", - "});", - "", - "console.log(\"==> Before checking response time\");", - "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", - " pm.expect(pm.response.responseTime).to.be.below(response_max);", - "});", - "", - "// Parse response body", - "console.log(\"==> Before getting response body\");", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "console.log(\"==> Before checking schema\");", - "pm.test(\"Validate Response Exam Type Schema\", function(){", - " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", - "});", - "", - "// Store all exam type IDs for future use in adding exams", - "console.log(\"==> Before allelements\");", - "var allExamIds = [];", - "", - "// Make sure some data returned.", - "console.log(\"==> Before checking there is an exam_types property\");", - "pm.test(\"Response has exam_types property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", - "});", - "console.log(\"==> Before checking at least one exam type\");", - "pm.test(\"Response has at least one exam_type\", function(){", - " pm.expect(jsonData.exam_types.length).to.be.above(0);", - "});", - "", - "// Set up list of valid exam types, create random functions.", - "console.log(\"==> setting environment variables\");", - "eval(environment.init_exam_type_data);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}exam_types/", - "host": [ - "{{url}}exam_types" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam test 5 updates", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (1-1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (1-2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (1-3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (1-4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (1-5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam test 3 updates", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (2-1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (2-2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (2-3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam tests 8 updates", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-6)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-7)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info (3-8)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam 1 test single update", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam 2 test single update", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Exam 3 test single update", - "item": [ - { - "name": "Create exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Get exam type data and functions.", - "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", - "eval(environment.create_random_functions);", - "", - "// Create an event number based on the time.", - "var ms = (new Date().getTime()).toString() + \"00\";", - "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", - "postman.setEnvironmentVariable(\"event_number\", eventNumber);", - "", - "// Update the next event ID.", - "var eventId = \"pm\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", 0);", - "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", - "postman.setEnvironmentVariable(\"event_delete\", eventId);", - "", - "// Calculate a random exam type to use.", - "random_index = get_random_index(exam_array);", - "", - "// Store for use.", - "random_exam_type_id = exam_array[random_index].id;", - "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"exam\")) {", - "\tcurrentExamId = jsonData.exam.exam_id;", - " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" - }, - "url": { - "raw": "{{url}}exams/", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Update exam info", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - }, - { - "name": "List exams", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "", - "// Update the next event ID.", - "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", - "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", - "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", - "postman.setEnvironmentVariable(\"update_number\", updateNumber);", - "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", - "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", - "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" - }, - "url": { - "raw": "{{url}}exams/?office_number={{current_office_number}}", - "host": [ - "{{url}}exams" - ], - "path": [ - "" - ], - "query": [ - { - "key": "office_number", - "value": "{{current_office_number}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete exam", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0ec5c421-a4af-4424-8201-86864e291f0e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}", - "type": "text" - } - ], - "url": { - "raw": "{{url}}exams/{{current_exam_id}}/", - "host": [ - "{{url}}exams" - ], - "path": [ - "{{current_exam_id}}", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file + "info": { + "_postman_id": "b8ea4228-164b-4447-a63e-d2c384530553", + "name": "Load_Test_Exam", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Setup", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "id": "5409b66d-67a4-449b-8633-9aeca632b388", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Complex-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "f006b951-3205-4f21-a315-369f85591929", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"complex_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Create-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.expect(pm.response.code).to.eql(201);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 201) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 201 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Type-Data", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"init_exam_type_data\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Finds an exam name in the JSON list allTypes of exam types.\nfunction find_exam(input_exam_name, allTypes) {\n\texam_id_value = -1;\n\n // Loop to look for the input exam name.\n if (allTypes) {\n allTypes.forEach(function(type) {\n if ((type.exam_type_name) == input_exam_name) {\n exam_id_value = type.exam_type_id;\n }\n });\n }\n \n // If the exam wasn't found, set it to be the first exam.\n if (exam_id_value == -1) {\n exam_id_value = allTypes[0].exam_type_id;\n }\n\n // Return the exam_id_type of the input exam name.\n return exam_id_value;\n}\n\n// Get the list of all possible exam types.\nallTypes = null;\nvar jsonData = JSON.parse(responseBody);\nif (jsonData.hasOwnProperty(\"exam_types\")) {\n\tallTypes = jsonData.exam_types;\n}\n\nexam_array = [];\nexam_array.push({name: \"Pesticide\", weight: 40.2, id: find_exam(\"Pesticide\", allTypes)});\nname = \"IPSE - 4HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[0].weight + 14.5, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[1].weight + 7.4, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[2].weight + 5.8, id: find_exam(name, allTypes)});\nname = \"IPSE - 4HR Group Exam\";\nexam_array.push({name: name, weight: exam_array[3].weight + 5.7, id: find_exam(name, allTypes)});\nname = \"COFQ - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[4].weight + 5.5, id: find_exam(name, allTypes)});\nname = \"Monthly Session Exam\";\nexam_array.push({name: name, weight: exam_array[5].weight + 3.8, id: find_exam(name, allTypes)});\nname = \"SLE - 3HR Single Exam\";\nexam_array.push({name: name, weight: exam_array[6].weight + 3.5, id: find_exam(name, allTypes)});\nname = \"Angling Guide Outfitter\";\nexam_array.push({name: name, weight: exam_array[7].weight + 2.4, id: find_exam(name, allTypes)});\nname = \"IPSE - 5HR Single Exam - Time Extension\";\nexam_array.push({name: name, weight: exam_array[8].weight + 2.3, id: find_exam(name, allTypes)});\nname = \"Exam Booking - 3 Hour Miscellaneous\";\nexam_array.push({name: name, weight: exam_array[9].weight + 1.7, id: find_exam(name, allTypes)});\n\n// Store the initialized exam data for later use.\npostman.setEnvironmentVariable(\"exam_array_data\", JSON.stringify(exam_array));" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Get-Random", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_random_functions\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Function returns a weighted randomized exam type index.\nfunction get_random_index(exam_array) {\n\n // Generate a random number up to the maximum weight allowed.\n random_number = Math.floor(Math.random() * exam_array[exam_array.length - 1].weight);\n \n // Get the index of the exam type corresponding to that weight.\n index = get_index(random_number, exam_array);\n\n // Return the index.\n return index;\n}\n\n// Based on a random number, turns it into a weighted randomized exam type index.\nfunction get_index(random_value, exam_array) {\n index = 0;\n var i;\n for (i = 0; i < exam_array.length; i++) {\n if (random_value <= exam_array[i].weight) {\n index = i;\n break;\n }\n }\n \n return index;\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exam\": {\n \"type\": \"object\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": [\"number\", \"null\"]},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exam\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-Data-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_data_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Make sure that jsonData has an exam property.\npm.test(\"Response should have exam property\", function(){\n pm.expect(jsonData.hasOwnProperty(\"exam\")).to.be.true;\n});\n\n// If jsonData has exam property, check data.\nif (jsonData.hasOwnProperty(\"exam\")) {\n\n //Test to see if Event ID field remains unchanged\n pm.test(\"Validate Event Id has expected value\", function(){\n pm.expect(jsonData.event_id === environment.event_id);\n });\n\n //Test to see if exam method field remains unchanged\n pm.test(\"Validate exam method has expected value\", function(){\n pm.expect(jsonData.exam_method === environment.exam_method);\n });\n\n //Test to see if exam name field remains unchanged\n pm.test(\"Validate exam name has expected value\", function(){\n pm.expect(jsonData.exam_name === environment.exam_name);\n });\n\n //Test to see if exam type field remains unchanged\n pm.test(\"Validate exam type id has expected value\", function(){\n pm.expect(jsonData.exam_type_id === environment.random_exam_type_id);\n });\n\n //Test to see if exam written indicator field remains unchanged\n pm.test(\"Validate exam written indicator has expected value\", function(){\n pm.expect(jsonData.exam_written_ind === environment.exam_written_ind);\n });\n\n //Test to see if examinee name field remains unchanged\n pm.test(\"Validate examinee name has expected value\", function(){\n pm.expect(jsonData.examinee_name === environment.examinee_name);\n });\n\n //Test to see if notes field remains unchanged\n pm.test(\"Validate notes has expected value\", function(){\n pm.expect(jsonData.notes === environment.notes);\n });\n\n //Test to see if number of students field remains unchanged\n pm.test(\"Validate number of students has expected value\", function(){\n pm.expect(jsonData.number_of_students === environment.number_of_students);\n });\n\n //Test to see if office id remains unchanged\n pm.test(\"Validate office id has expected value\", function(){\n pm.expect(jsonData.office_id === environment.current_office_id);\n });\n\n //Test to see if offsite location is expected\n pm.test(\"Validate offsite location has expected value\", function(){\n pm.expect(jsonData.offsite_location === environment.offsite_location);\n });\n}\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Exam-List-Schema-Check", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"exam_schema_list_check\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Define the JSON Schema expected in response\nvar examSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"exams\": {\n \"type\": \"array\",\n \"properties\": {\n \"booking_id\": {},\n \"deleted_date\": {},\n \"event_id\": {\"type\": \"string\"},\n \"exam_id\": {\"type\": \"number\"},\n \"exam_method\": {\"type\": \"string\"},\n \"exam_name\": {\"type\": \"string\"},\n \"exam_received_date\": {},\n \"exam_returned_tracking_number\": {},\n \"exam_type\": {\n \"type\": \"object\",\n \"properties\": {\n \"exam_color\": {\"type\": \"string\"},\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_type_name\": {\"type\": \"string\"},\n \"group_exam_ind\": {\"type\": \"number\"},\n \"ita_ind\": {\"type\": \"number\"},\n \"method_type\": {\"type\": \"string\"},\n \"number_of_hours\": {\"type\": \"number\"},\n \"number_of_minutes\": {\"type\": \"number\"},\n \"pesticide_exam_ind\": {\"type\": \"number\" }\n },\n \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"group_exam_ind\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"number_of_minutes\", \"pesticide_exam_ind\"]\n },\n \"exam_type_id\": {\"type\": \"number\"},\n \"exam_written_ind\": {\"type\": \"number\"},\n \"examinee_name\": {\"type\": \"string\"},\n \"expiry_date\": {\"type\": [\"string\", \"null\"]},\n \"notes\": {\"type\": \"string\"},\n \"number_of_students\": {\"type\": \"number\"},\n \"office\":{\n \"type\": \"object\",\n \"properties\": {\n \"appointments_enabled_ind\": {\"type\": \"number\"},\n \"exams_enabled_ind\": {\"type\": \"number\"},\n \"office_id\": {\"type\": \"number\"},\n \"office_name\": {\"type\": \"string\"},\n \"office_number\": {\"type\": \"number\" },\n \"timezone\": {\n \"type\": \"object\",\n \"properties\": {\n \"timezone_id\": {\"type\" : \"number\"},\n \"timezone_name\": {\"type\": \"string\"}\n },\n \"required\": [\"timezone_id\", \"timezone_name\"]\n },\n },\n \"required\": []\n },\n \"office_id\": {\"type\": \"number\"},\n \"offsite_location\": {},\n \"session_number\": {\"type\": [\"number\", \"null\"]},\n \"booking\": {}\n },\n \"required\": [\"booking\", \"booking_id\", \"deleted_date\", \"event_id\", \"exam_id\", \"exam_method\", \"exam_name\", \"exam_received_date\", \"exam_returned_tracking_number\", \"exam_type\", \"exam_type_id\", \"exam_written_ind\", \"examinee_name\", \"expiry_date\", \"notes\", \"number_of_students\", \"office\", \"office_id\", \"offsite_location\", \"session_number\"]\n }\n },\n \"required\": [\"exams\"]\n};\n\n//Test to see if response schema is valid\npm.test(\"Validate Response Exam List Schema\", function(){\n pm.expect(tv4.validate(jsonData, examSchema)).to.be.true;\n});\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "Who am I", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ], + "description": "This folder performs basic authentication features.", + "protocolProfileBehavior": {} + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Set health response time variable.", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum load time allowed.", + "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints", + "protocolProfileBehavior": {} + }, + { + "name": "Check user login", + "item": [ + { + "name": "Authenticate user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "description": "Make sure the operator ID can log in" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Get Exam Types", + "item": [ + { + "name": "Exam Type List", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2df2a851-6a71-48e6-b44f-ccf096fe9d83", + "exec": [ + "// Define the JSON Schema expected in response", + "var examTypeSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"exam_color\": {\"type\": \"string\"},", + " \"exam_type_id\": {\"type\": \"number\"},", + " \"exam_type_name\": {\"type\": \"string\"},", + " \"ita_ind\": {\"type\": \"number\"},", + " \"method_type\": {\"type\": \"string\"},", + " \"number_of_hours\": {\"type\": \"number\"},", + " \"group_exam_ind\": {\"type\": \"number\"}", + " },", + " \"required\": [\"exam_color\", \"exam_type_id\", \"exam_type_name\", \"ita_ind\", \"method_type\", \"number_of_hours\", \"group_exam_ind\"]", + " },", + " \"required\": []", + "}; ", + "", + "// Get the max response time allowed.", + "console.log(\"==> Before getting response max\");", + "var response_max = JSON.parse(globals.max_response_time);", + "", + "// Check Response code for request", + "console.log(\"==> Before checking response code\");", + "pm.test(\"Response code for request is 200\", function(){", + " pm.response.to.have.status(200);", + "});", + "", + "console.log(\"==> Before checking response time\");", + "pm.test(\"Response time less than \" + response_max.toString() + \"ms\", function(){", + " pm.expect(pm.response.responseTime).to.be.below(response_max);", + "});", + "", + "// Parse response body", + "console.log(\"==> Before getting response body\");", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "console.log(\"==> Before checking schema\");", + "pm.test(\"Validate Response Exam Type Schema\", function(){", + " pm.expect(tv4.validate(jsonData, examTypeSchema)).to.be.true;", + "});", + "", + "// Store all exam type IDs for future use in adding exams", + "console.log(\"==> Before allelements\");", + "var allExamIds = [];", + "", + "// Make sure some data returned.", + "console.log(\"==> Before checking there is an exam_types property\");", + "pm.test(\"Response has exam_types property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"exam_types\")).to.be.true;", + "});", + "console.log(\"==> Before checking at least one exam type\");", + "pm.test(\"Response has at least one exam_type\", function(){", + " pm.expect(jsonData.exam_types.length).to.be.above(0);", + "});", + "", + "// Set up list of valid exam types, create random functions.", + "console.log(\"==> setting environment variables\");", + "eval(environment.init_exam_type_data);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}exam_types/", + "host": [ + "{{url}}exam_types" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam test 5 updates", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (1-1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}\n" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (1-2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (1-3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (1-4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (1-5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam test 3 updates", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (2-1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (2-2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (2-3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam tests 8 updates", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-6)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-7)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info (3-8)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite locatoin - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam 1 test single update", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam 2 test single update", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Exam 3 test single update", + "item": [ + { + "name": "Create exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"event_id\",\"random_exam_type_id\",\"current_office_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Get exam type data and functions.", + "exam_array = JSON.parse(postman.getEnvironmentVariable(\"exam_array_data\"));", + "eval(environment.create_random_functions);", + "// Create an event number based on the time.", + "var ms = (new Date().getTime()).toString() + \"00\";", + "eventNumber = Number(ms.substring(ms.length-6, ms.len)) + 1;", + "postman.setEnvironmentVariable(\"event_number\", eventNumber);", + "// Update the next event ID.", + "var eventId = \"pm\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", 0);", + "postman.setEnvironmentVariable(\"event_id\", JSON.stringify(eventId));", + "postman.setEnvironmentVariable(\"event_delete\", eventId);", + "// Calculate a random exam type to use.", + "random_index = get_random_index(exam_array);", + "// Store for use.", + "random_exam_type_id = exam_array[random_index].id;", + "postman.setEnvironmentVariable(\"random_exam_type_id\", JSON.stringify(random_exam_type_id.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"exam\")) {", + "\tcurrentExamId = jsonData.exam.exam_id;", + " postman.setEnvironmentVariable(\"current_exam_id\", currentExamId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\n\t\"event_id\" : {{event_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : \"Postman Group Exam Name\",\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : \"Pm examinee name\",\n \"notes\" : \"Pm sample notes\",\n \"number_of_students\" : \"19\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : \"Pm test location\"\n}" + }, + "url": { + "raw": "{{url}}exams/", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Update exam info", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\",\"update_id\",\"exam_name\",\"random_exam_type_id\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : {{random_exam_type_id}},\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + }, + { + "name": "List exams", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_office_number\",\"operator_token\",\"update_id\",\"exam_name\",\"examinee_name\",\"notes\",\"current_office_id\",\"offsite_location\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// Update the next event ID.", + "var eventNumber = JSON.parse(postman.getEnvironmentVariable(\"event_number\"));", + "var updateNumber = JSON.parse(postman.getEnvironmentVariable(\"update_number\")) + 1;", + "var updateEventId = \"pm-up\" + updateNumber.toString() + \"-\" + eventNumber.toString();", + "postman.setEnvironmentVariable(\"update_number\", updateNumber);", + "postman.setEnvironmentVariable(\"update_id\", JSON.stringify(updateEventId));", + "postman.setEnvironmentVariable(\"exam_name\", JSON.stringify(\"PM exam name - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"examinee_name\", JSON.stringify(\"PM examinee - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"notes\", JSON.stringify(\"PM exam notes - Update \" + updateNumber.toString()));", + "postman.setEnvironmentVariable(\"offsite_location\", JSON.stringify(\"PM offsite location - Update \" + updateNumber.toString()));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"event_id\" : {{update_id}},\n \"exam_method\" : \"paper\",\n \"exam_name\" : {{exam_name}},\n \"exam_type_id\" : \"6\",\n \"exam_written_ind\" : \"0\",\n \"examinee_name\" : {{examinee_name}},\n \"notes\" : {{notes}},\n \"number_of_students\" : \"119\",\n \"office_id\" : {{current_office_id}},\n \"offsite_location\" : {{offsite_location}}\n}" + }, + "url": { + "raw": "{{url}}exams/?office_number={{current_office_number}}", + "host": [ + "{{url}}exams" + ], + "path": [ + "" + ], + "query": [ + { + "key": "office_number", + "value": "{{current_office_number}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete exam", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0ec5c421-a4af-4424-8201-86864e291f0e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_exam_id\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{url}}exams/{{current_exam_id}}/", + "host": [ + "{{url}}exams" + ], + "path": [ + "{{current_exam_id}}", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} diff --git a/api/postman/Load_Test_TheQ.json b/api/postman/Load_Test_TheQ.json index 395a0b3f8..60337af14 100644 --- a/api/postman/Load_Test_TheQ.json +++ b/api/postman/Load_Test_TheQ.json @@ -1,4817 +1,5202 @@ { - "info": { - "_postman_id": "cda6659f-7e26-412f-9a1c-979b54507ea2", - "name": "Load_Test_TheQ", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Setup", - "item": [ - { - "name": "Setup-Variables", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", - "exec": [ - "// See if the use-prefix global has been set. Use default if not.", - "let usePrefix = '';", - "if (pm.globals.get('use-prefix')) {", - " console.log(\"==> use-prefix exists\");", - " usePrefix = pm.globals.get('use-prefix');", - " console.log(\" --> Prefix is: \" + usePrefix);", - " ", - " // Set up all globals, using the correct prefix.", - " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", - " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", - " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", - " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", - " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", - "}", - "else {", - " console.log(\"==> use-prefix does not exist\");", - " console.log(\" --> No default globals set.\");", - "}", - "", - "// If no maximum load time defined, set a default.", - "if (!pm.globals.get('max_load_time')) {", - " console.log(\"==> max_load_time not present, default set.\");", - " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", - "}", - "", - "// If no maximum response defined, set a default.", - "if (!pm.globals.get('max_response_time')) {", - " console.log(\"==> max_response_time not present, default set.\");", - " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", - "}", - "", - "// Display the values of all globals.", - "console.log(\"\");", - "console.log(\"==> Globals are:\");", - "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", - "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", - "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", - "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", - "console.log(\" --> url: \" + pm.globals.get(\"url\"));", - "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", - "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Dummy data." - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-First", - "event": [ - { - "listen": "test", - "script": { - "id": "5409b66d-67a4-449b-8633-9aeca632b388", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_first\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n\nconst echoPostRequest = {\n url: auth_url + '/auth/realms/' + realm + '/protocol/openid-connect/token',\n method: 'POST',\n header: 'Content-Type:application/x-www-form-urlencoded',\n body: {\n mode: 'raw',\n raw: 'grant_type=password&client_id=' + clientid \n + '&username=' + userid \n + '&password=' + password\n + '&client_secret=' + client_secret\n }\n};\npm.sendRequest(echoPostRequest, function (err, res) {\n var jsonData = res.json();\n if (jsonData.hasOwnProperty('access_token')) {\n \tpm.globals.set(\"token\", jsonData.access_token);\n\t pm.globals.set(\"refresh_token\", jsonData.refresh_token);\n\t if (err) {\n\t console.log(err);\n\t }\n\t // console.log(err ? err : res.json());\n\t} else {\n\t pm.globals.set(\"token\", 0);\n\t pm.globals.set(\"refresh_token\", 0);\n\t pm.globals.set(\"token_expires\", 0);\n\t pm.globals.set(\"refresh_token_expires\", 0);\n\t}\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Auth-Script", - "event": [ - { - "listen": "test", - "script": { - "id": "f386cf22-bb0f-47d9-9c22-cda162ed4375", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"auth_script\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "auth_url = globals.auth_url;\nrealm = globals.realm;\nclientid = globals.clientid;\nuserid = globals.userid;\npassword = globals.password;\nclient_secret = globals.client_secret;\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Basic-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"basic_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\r\nmax_response_time = JSON.parse(globals.max_response_time);\r\n\r\n// Check to make sure the response time was within the maximum allowed.\r\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\r\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\r\n});\r\n\r\n// Other tests.\r\npm.test(\"Response code for request is 200\", function(){\r\n pm.response.to.have.status(200);\r\n});\r\npm.test('Response header should have Content-Type of application/json', function() {\r\n pm.response.to.have.header('content-type', 'application/json');\r\n});\r\npm.test('Response body be in JSON format', function() {\r\n pm.response.to.be.json; \r\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Complex-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "f006b951-3205-4f21-a315-369f85591929", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"complex_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response code for request is 200\", function(){\n pm.response.to.have.status(200);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Create-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "382b7b65-d736-4a03-a44a-3891f33b617d", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"create_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\n// Check to make sure the response time was within the maximum allowed.\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\n// Other tests.\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.response.to.have.status(201);\n});\npm.test('Response header should have Content-Type of application/json', function() {\n pm.response.to.have.header('content-type', 'application/json');\n});\npm.test('Response body be in JSON format', function() {\n pm.response.to.be.json; \n});\n" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Citizen-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "8cebcf78-f5a2-4694-b108-60e146791a78", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"citizen_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var schema = {\n \"properties\" : {\n \"start_time\" : {\n \"type\" : \"string\"\n },\n \"citizen_name\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"number\", \"object\"]\n },\n \"qt_xn_citizen_ind\" : {\n \"type\" : \"number\"\n },\n \"ticket_number\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"service_reqs\" : {\n \"type\" : \"array\"\n },\n \"office_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"cs\" : {\n \"type\" : \"object\"\n },\n \"citizen_comments\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \n },\n \"required\" : [\"start_time\", \"citizen_name\", \"citizen_id\",\n \"qt_xn_citizen_ind\", \"ticket_number\", \"service_reqs\",\n \"office_id\", \"cs\", \"citizen_comments\"]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n \n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Service-Response-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "0e6c4264-28b3-4a49-b6bb-1199aef2cb13", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"service_response_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "var schema = {\n \"properties\" : {\n \"sr_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"sr_state\" : {\n \"type\" : \"object\"\n },\n \"periods\" : {\n \"type\" : \"array\"\n },\n \"service\" : {\n \"type\" : \"object\"\n },\n \"citizen\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"quantity\" : {\n \"type\" : \"number\"\n },\n \"service_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"channel\" : {\n \t\"type\" : \"object\"\n },\n \"channel_id\" : {\n \t\"type\" : [\"object\", \"number\"]\n }\n \n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - }, - { - "name": "CFMS-Install-Get-Active-Citizens-Tests", - "event": [ - { - "listen": "test", - "script": { - "id": "8856712d-73ca-4172-89ba-590e8b015c6b", - "exec": [ - "var jsonData = pm.response.json();", - "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" - }, - "url": { - "raw": "https://postman-echo.com/post", - "protocol": "https", - "host": [ - "postman-echo", - "com" - ], - "path": [ - "post" - ] - }, - "description": "Sets the authentication script" - }, - "response": [] - } - ], - "description": "This folder performs basic authentication features.", - "protocolProfileBehavior": {} - }, - { - "name": "Check app health", - "item": [ - { - "name": "Check healthz driver TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum response time allowed.", - "max_load_time = JSON.parse(globals.max_load_time);", - "", - "// Set health response time variable.", - "health_tries = 15;", - "counter = 1;", - "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long. Try again, give pod a chance to spin up.", - "else {", - " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", - "}" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the healthz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", - "exec": [ - "// Get the maximum load time allowed.", - "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", - "", - "// Get and update variables.", - "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", - "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", - "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is healthy'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is healthy');", - "});", - "", - "// If response time is OK, proceed to the next test.", - "if (pm.response.responseTime < max_load_time) {", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - "}", - " ", - "// Response time is too long.", - "else {", - " ", - " // You haven't reached your maximum tries yet. Try again.", - " if (counter < health_tries) {", - " postman.setNextRequest(\"Check the healthz endpoint\");", - " }", - " ", - " // You have reached the maximum. An error, go to next test.", - " else {", - " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", - " pm.expect(counter).to.be.below(health_tries);", - " });", - " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", - " }", - "}", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}healthz/", - "host": [ - "{{url}}healthz" - ], - "path": [ - "" - ] - } - }, - "response": [] - }, - { - "name": "Check the readyz endpoint TheQ", - "event": [ - { - "listen": "test", - "script": { - "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", - "exec": [ - "// Perform the standard tests.", - "eval(environment.basic_response_test);", - "", - "var jsonData = JSON.parse(responseBody);", - "", - "// Test the health response.", - "pm.test(\"Response should have 'message' property\", function() {", - " pm.expect(jsonData).to.have.property('message');", - "});", - "", - "pm.test(\"Response message should be 'api is ready'\", function() {", - " pm.expect(jsonData.message).to.be.eql('api is ready');", - "});", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}readyz/", - "host": [ - "{{url}}readyz" - ], - "path": [ - "" - ] - } - }, - "response": [] - } - ], - "description": "Checks the application health by calling the healthz and readyz endpoints", - "protocolProfileBehavior": {} - }, - { - "name": "Check user login", - "item": [ - { - "name": "Authenticate default QTxn user", - "event": [ - { - "listen": "test", - "script": { - "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", - "exec": [ - "// Do the basic checks.", - "eval(environment.basic_response_test);", - "", - "// Get the response.", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to make sure that the access token field is not null", - "pm.test(\"Access Token is not null\", function(){", - " var access_token = jsonData.access_token;", - " pm.expect(access_token).not.eql(null);", - "});", - "", - "//Test to make sure that the refresh token response field is not null", - "pm.test(\"Refresh Token is not null\", function(){", - " var refresh_token = jsonData.refresh_token;", - " pm.expect(refresh_token).not.eql(null);", - "});", - "", - "//Test to make sure that expires in response field is not nullf", - "pm.test(\"Expires In is not null\", function(){", - " var expires_in = jsonData.expires_in;", - " pm.expect(expires_in).not.eql(null);", - "});", - "", - "//Test to make sure that refresh expires in response fiels is not null", - "pm.test(\"Refresh Expires In is not null\", function(){", - " var refresh_expires_in = jsonData.refresh_expires_in;", - " pm.expect(refresh_expires_in).not.eql(null);", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ], - "body": { - "mode": "raw", - "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" - }, - "url": { - "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", - "host": [ - "{{auth_url}}" - ], - "path": [ - "auth", - "realms", - "{{realm}}", - "protocol", - "openid-connect", - "token" - ], - "query": [ - { - "key": "Content-Type", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "description": "Make sure the operator ID can log in" - }, - "response": [] - }, - { - "name": "Who am I TheQ", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - "\tcurrentCsrId = jsonData.csr.csr_id;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", - "};", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check channels", - "item": [ - { - "name": "Get channels", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "2b71673c-4aa2-47b3-8594-15f02b390ee0", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "42742c4c-505d-472d-857b-fdba74cc49ac", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"channel_name\" : {", - " \"type\" : \"string\"", - " },", - " \"channel_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " }", - " },", - " \"required\" : [\"channel_name\", \"channel_id\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allChannels = jsonData.channels;", - "var channelCount = 0;", - "var phoneId = 0;", - "var emailId = 0;", - "var phoneText = \"Phone\";", - "var emailText = \"Email/Fax/Mail\";", - "allChannels.forEach(function(channel) {", - " channelCount ++;", - " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", - " tests[testTitle] = tv4.validate(channel, schema);", - " if (channel.channel_name === phoneText) {", - " phoneId = channel.channel_id;", - " }", - " if (channel.channel_name === emailText) {", - " emailId = channel.channel_id;", - " }", - "});", - "", - "// Check that you found the phone ID.", - "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", - " pm.expect(phoneId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the email ID.", - "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", - " pm.expect(emailId).to.not.be.eql(0);", - "});", - "", - "// Store this ID for future use.", - "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", - "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}channels/", - "host": [ - "{{url}}channels" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check counters", - "item": [ - { - "name": "Store CSR and Office Info", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "c34723d3-c0d1-4504-97a2-373088309a5b", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", - "exec": [ - "// Define the JSON Schema expected in response", - "var CSRSSchema = {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_id\": {\"type\": \"number\"},", - " \"csr_state\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"csr_state_desc\": {\"type\": \"string\"},", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"csr_state_name\": {\"type\": \"string\"}", - " },", - " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", - " },", - " \"csr_state_id\": {\"type\": \"number\"},", - " \"deleted\": {},", - " \"finance_designate\": {\"type\": \"number\"},", - " \"office_manager\": {\"type\": \"number\"},", - " \"ita2_designate\": {\"type\": \"number\"},", - " \"office\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"appointments_enabled_ind\": {\"type\": \"number\"},", - " \"exams_enabled_ind\": {\"type\": \"number\"},", - " \"office_id\": {\"type\": \"number\"},", - " \"office_name\": {\"type\": \"string\"},", - " \"office_number\": {\"type\": \"number\"},", - " \"sb\":{", - " \"type\": \"object\",", - " \"properties\": {", - " \"sb_id\": {\"type\": \"number\"},", - " \"sb_type\": {\"type\": \"string\"}", - " },", - " \"required\": [\"sb_id\", \"sb_type\"]", - " }", - " },", - " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", - " },", - " \"office_name\": {\"type\": \"string\"},", - " \"pesticide_designate\": {\"type\": \"number\"},", - " \"qt_xn_csr_ind\": {\"type\": \"number\"},", - " \"receptionist_ind\": {\"type\": \"number\"},", - " \"role\": {", - " \"type\": \"object\",", - " \"properties\": {", - " \"role_code\": {\"type\": \"string\"},", - " \"role_desc\": {\"type\": \"string\"},", - " \"role_id\": {\"type\": \"number\"}", - " },", - " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", - " },", - " \"role_id\": {\"type\": \"number\"},", - " \"username\": {\"type\": \"string\"}", - " },", - " \"attention_needed\": {\"type\": \"boolean\"},", - " \"required\": [\"csr\", \"attention_needed\"]", - "};", - "", - "// Run basic response tests.", - "eval(environment.basic_response_test);", - "", - "// Parse response body", - "var jsonData = JSON.parse(responseBody);", - "", - "//Test to see if response schema is valid", - "pm.test(\"Validate Response CSRs Schema\", function(){", - " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", - "});", - "", - "// Make sure that jsonData has an csr property.", - "pm.test(\"Response should have csr property\", function(){", - " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", - "});", - "", - "var csr = 0;", - "var office = 0;", - "var counters = 0;", - "var counter_text = \"Counter\";", - "var counter_id = 0;", - "var qtxn_text = \"Quick Trans\";", - "var qtxn_id = 0;", - "", - "if (jsonData.hasOwnProperty(\"csr\")) {", - "\tcurrentOfficeId = jsonData.csr.office_id;", - "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", - " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", - " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", - " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", - " ", - " csr = jsonData.csr;", - " counter_id = 0;", - " qtxn_id = 0;", - "", - " // Make sure that jsonData has an booking property.", - " pm.test(\"CSR should have office property\", function(){", - " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", - " });", - " ", - " // Make sure office has counter property.", - " if (csr.hasOwnProperty(\"office\")) {", - " office = csr.office;", - " ", - " // Make sure that jsonData has an booking property.", - " pm.test(\"Office should have counters property\", function(){", - " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", - " });", - "", - " // Make sure office has counter property.", - " if (office.hasOwnProperty(\"counters\")) {", - " counters = office.counters;", - " ", - " // Search for Counter and Quick Trans counters", - " counters.forEach(function(counter) {", - " if (counter.counter_name === counter_text) {", - " counter_id = counter.counter_id;", - " }", - " if (counter.counter_name === qtxn_text) {", - " qtxn_id = counter.counter_id;", - " }", - " });", - " ", - " // Make sure you found the right IDs.", - " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", - " pm.expect(counter_id).to.not.be.eql(0);", - " });", - " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", - " pm.expect(qtxn_id).to.not.be.eql(0);", - " });", - " ", - " // Store the ids for future use.", - " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", - " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", - " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", - " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", - " }", - "", - " ", - " }", - "", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}csrs/me/", - "host": [ - "{{url}}csrs" - ], - "path": [ - "me", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check categories", - "item": [ - { - "name": "Get categories", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "e99f23ee-5a03-43d9-9075-75dbf06aadcc", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1a9c9ff5-8475-4dd2-a6c7-ffb5d4689145", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allCategories = jsonData.categories;", - "var categoryCount = 0;", - "allCategories.forEach(function(category) {", - " categoryCount ++;", - " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", - " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", - " var displayInd = category.display_dashboard_ind;", - " var serviceInd = category.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.eql(0);", - " });", - "", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(0);", - " });", - " ", - " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", - " pm.expect(category.parent_id).to.be.null;", - " });", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}categories/", - "host": [ - "{{url}}categories" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check services", - "item": [ - { - "name": "Get services", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_first);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3b11031e-4031-4569-91f2-49e23517ffee", - "exec": [ - "// Run basic tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "var schema = {", - " \"properties\" : {", - " \"display_dashboard_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"deleted\" : {", - " \"type\" : [\"object\", \"null\"]", - " },", - " \"actual_service_ind\" : {", - " \"type\" : \"number\"", - " },", - " \"service_id\" : {", - " \"type\" : [\"number\", \"object\"]", - " },", - " \"service_code\" : {", - " \"type\" : \"string\"", - " },", - " \"prefix\" : {", - " \"type\" : \"string\"", - " },", - " \"service_name\" : {", - " \"type\" : \"string\"", - " },", - " \"parent_id\" : {", - " \"type\" : [\"object\", \"number\", \"null\" ]", - " },", - " \"service_desc\" : {", - " \"type\" : \"string\"", - " }", - " },", - " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", - " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", - "};", - "", - "// Loop to validate schema of each channel.", - "var allElements = jsonData.services;", - "var elementCount = 0;", - "var elementMax = Math.min(10, allElements.length);", - "//allElements.forEach(function(element) {", - "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", - " element = allElements[currentElement];", - " elementCount ++;", - " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", - " tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);", - " displayInd = element.display_dashboard_ind;", - " serviceInd = element.actual_service_ind;", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", - " pm.expect(displayInd).to.be.within(0, 1);", - " });", - "", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", - " pm.expect(serviceInd).to.be.eql(1);", - " });", - " ", - " // Test that returned data is valid.", - " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", - " pm.expect(element.parent_id).to.not.be.null;", - " });", - "}", - "", - "// Declare and initialize variables.", - "var mspId = 0;", - "var taxId = 0;", - "var mspText = \"Payment - MSP\";", - "var propTaxText = \"Other - PTAX\";", - "", - "// Look for the MSP and Property Tax IDs.", - "allElements.forEach(function(element) {", - " if (element.service_name === mspText) {", - " mspId = element.service_id;", - " }", - " if (element.service_name === propTaxText) {", - " taxId = element.service_id;", - " }", - "});", - "", - "// Check that you found the MSP service.", - "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", - " pm.expect(mspId).to.not.be.eql(0);", - "});", - "", - "// Check that you found the Property Tax service.", - "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", - " pm.expect(taxId).to.not.be.eql(0);", - "});", - "", - "// Store these IDs for future use.", - "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", - "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - } - ], - "url": { - "raw": "{{url}}services/", - "host": [ - "{{url}}services" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check citizen through queue (QT1)", - "item": [ - { - "name": "Create citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "7896261d-7d88-4eba-860b-98472655c4a7", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "b72e7756-5de1-4a13-9609-8917ea63dee7", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "73110fdd-6476-403d-b6c9-c108032fbd66", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "66ca9683-be00-478b-92c0-6ac698d9088b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8a294876-edfa-4d5b-ada4-399f5339cd8f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Invite specific citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8e9cd1ef-a5d9-4f81-b061-f818d4fc5603", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Invited\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/invite/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "invite", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1b1f7f66-04cd-4f10-bfa2-c4f3fffe8715", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT1)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "818713b9-18a0-4fef-9c16-f71b43dd2ad0", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check citizen begin-hold-finish (QT2)", - "item": [ - { - "name": "Create citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e77d15de-91e2-4528-87bd-5734405b4def", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " ", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "} ", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "ce732be5-38a3-4825-90d1-4d52babf8917", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "13828662-748d-4b3f-9ead-454e795f1f01", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2a57d1b2-9d0c-4e07-ad20-d4dde457f9d1", - "exec": [ - "// Install postmanBDD, json-bigint parse and stringify.", - "eval(globals.postmanBDD);", - "eval(globals.json_bigint_parse);", - "", - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "4550a6b7-560d-48bf-bfc9-41453a8defec", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Place citizen on hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "dd51a48d-2e1e-47fd-978f-507b77a7901f", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 3 (now three periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "place_on_hold", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Get service requests (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "f4809d17-b59d-47fc-861e-5edaf421bddc", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get periods for the first service request.", - " var allPeriods = allElements[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " var allPeriodCount = 0;", - "", - " // Find how many periods there are with null end time.", - " // Also, check schema.", - " allPeriods.forEach(function(onePeriod) {", - " ", - " // Find the open period.", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " ", - " });", - "} ", - "", - "// If there are some service requests, proceed with tests.", - "if (allElements !== null) {", - "", - " // Perform tests.", - " pm.test('There must be only one service request', function() {", - " pm.expect(allElements.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have three periods', function() {", - " pm.expect(allPeriods.length).to.be.eql(3);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('Service request period state must be \"On hold\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/service_requests/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "service_requests", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Call citizen from hold (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "66563797-2a92-4dbd-946e-7232dcac8260", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (now four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT2)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "ec865f9c-a4b6-421f-956e-783972df2ad3", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = allElements[0].service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 4 (still four periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(4);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check citizen leave after create (QT3)", - "item": [ - { - "name": "Create citizen (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "061a479c-37b4-4707-b8c6-c31cacadecec", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT3)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "16eaefc2-0d16-405e-9e70-e85314da841b", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = null;", - " if (currentCitizen.service_reqs.length !== 0) {", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " }", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " if (allPeriods !== null) {", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - " }", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check citizen leave after waiting (QT4)", - "item": [ - { - "name": "Create citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fcc1415c-0861-4516-8843-bfc3717a76d9", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "f26eaedb-5d41-4dcc-b856-aec599440601", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// }", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "e0b48324-0ca2-4b2e-88ef-cbfbe10b5fb7", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "1176fcca-52ff-4e13-b4a4-775ad324a4c6", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add citizen to queue (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "f0aacc69-ff0e-4ed3-9ef7-f131bbf3a5bd", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Pending\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Waiting\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "add_to_queue", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Citizen left (QT4)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "4ee90115-54c2-4b44-b6f1-d739e8a385b1", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test(\"Must be no active citizens in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test(\"Citizen should have one service request\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/citizen_left/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "citizen_left", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Check update service information (QT5)", - "item": [ - { - "name": "Create citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "d3bbd88a-25fd-4fe6-ae7e-359373df02ac", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - "", - " // Perform tests.", - " pm.test(\"Only one citizen should be in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test(\"Current citizen name should be null\", function() {", - " pm.expect(currentCitizen.citizen_name).to.be.null;", - " });", - - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "", - " // Store the ID of the citizen just created.", - " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{}" - }, - "url": { - "raw": "{{url}}citizens/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Edit specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "2be395b2-e14c-4ff8-8753-42ea1fe2010d", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "", - "// // Get data, create JSON body.", - "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", - "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", - "// var bodyData = {", - "// \"citizen_name\" : citizenName,", - "// \"citizen_comments\" : citizenComments", - "// };", - "", - "// // Store the data in an environment variable.", - "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "2e7878d3-653e-479e-850f-c454b378217e", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - "", - " // Perform tests.", - " pm.test(\"Must be one active citizen in the office\", function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test(\"Citizen should have no service requests\", function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Add property tax via phone service request (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", - "exec": [ - "// Run complex tests.", - "eval(environment.create_response_test);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" - }, - "url": { - "raw": "{{url}}service_requests/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "List specific citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "3d906a10-51fe-4329-bac0-e69e6847e852", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - " ", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request must have one period', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", - " });", - " pm.test('Service request period channel must be ' + citizenChannel, function() {", - " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", - " });", - " pm.test('Service request period state must be \"Ticket Creation\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", - " });", - "", - " // Save the service request ID for later.", - " var mySRId = allElements[0].service_reqs[0].sr_id;", - " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "url": { - "raw": "{{url}}citizens/{{current_client}}/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Begin serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "ff3a25ca-0df2-40f2-b7a1-9e9b4bde2aab", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = null;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Run citizen tests.", - " eval(environment.get_active_citizens_test);", - " ", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be one active citizen in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(1);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", - " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", - " });", - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Active\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (now two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must only be one open period', function() {", - " pm.expect(openPeriodCount).to.be.eql(1);", - " });", - " pm.test('The open period state must be \"Being Served\"', function() {", - " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "", - "value": "", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/begin_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "begin_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update quantity from 3 to 5 (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "153a139d-0daa-4031-ba0a-e7204ade2648", - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - "", - " // Perform tests.", - " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Update service from PropTax to MSP (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "8faaa693-a7a4-45ed-9318-325a498a98af", - "exec": [ - "// Run complex tests.", - "eval(environment.basic_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.service_response_test);", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Only check for an updated quantity. Get environment variables.", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - "", - " // Perform tests.", - " pm.test('Updated service request service must be ' + citizenService, function() {", - " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" - }, - "url": { - "raw": "{{url}}service_requests/{{current_sr_id}}/", - "host": [ - "{{url}}service_requests" - ], - "path": [ - "{{current_sr_id}}", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - }, - { - "name": "Finish serving citizen (QT5)", - "event": [ - { - "listen": "prerequest", - "script": { - "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", - "exec": [ - "// Ensure the client is logged in.", - "eval(environment.auth_script);", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "id": "7a831dcf-ff2c-4907-95c5-bf042cdd967a", - "exec": [ - "// Run complex tests.", - "eval(environment.complex_response_test);", - "", - "// Get json return data.", - "var jsonData = JSON.parse(responseBody);", - "", - "// Run citizen tests.", - "eval(environment.citizen_response_test);", - "", - "// Declare, initialize variables.", - "var citizenIds = [];", - "var currentCitizen = jsonData.citizen;", - "", - "// If there are some citizens, proceed with tests.", - "if (allElements !== null) {", - "", - " // Get environment variables.", - " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", - " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", - " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", - " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", - " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", - " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", - " var allPeriods = currentCitizen.service_reqs[0].periods;", - " var openPeriod = null;", - " var openPeriodCount = 0;", - " ", - " // Find how many periods there are with null end time.", - " allPeriods.forEach(function(onePeriod) {", - " if (!onePeriod.time_end) {", - " openPeriod = onePeriod;", - " openPeriodCount++;", - " }", - " });", - "", - " // Perform tests.", - " pm.test('Must be no active citizens in the office', function() {", - " pm.expect(citizenIds.length).to.be.eql(0);", - " });", - " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", - " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", - " });", - " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", - " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", - " });", - - " pm.test('There must be only one service request', function() {", - " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", - " });", - " pm.test('Service request state must be \"Complete\"', function() {", - " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", - " });", - " pm.test('Service request service must be ' + citizenService, function() {", - " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", - " });", - " pm.test('Service request quantity must be ' + citizenQuantity, function() {", - " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", - " });", - " pm.test('Service request periods length must be 2 (should be two periods)', function() {", - " pm.expect(allPeriods.length).to.be.eql(2);", - " });", - " pm.test('There must be no open periods', function() {", - " pm.expect(openPeriodCount).to.be.eql(0);", - " });", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{token}}" - }, - { - "key": "Accept", - "value": "application/json, text/plain, */*" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}citizens/{{current_client}}/finish_service/", - "host": [ - "{{url}}citizens" - ], - "path": [ - "{{current_client}}", - "finish_service", - "" - ] - }, - "description": "Get a list of clients in the queue" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file + "info": { + "_postman_id": "cda6659f-7e26-412f-9a1c-979b54507ea2", + "name": "Load_Test_TheQ", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Setup", + "item": [ + { + "name": "Setup-Variables", + "event": [ + { + "listen": "test", + "script": { + "id": "5409b66d-67a4-449b-8633-9aeca632b388", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e9eed831-7955-4218-a400-ae6e1e7e2e10", + "exec": [ + "// See if the use-prefix global has been set. Use default if not.", + "let usePrefix = '';", + "if (pm.globals.get('use-prefix')) {", + " console.log(\"==> use-prefix exists\");", + " usePrefix = pm.globals.get('use-prefix');", + " console.log(\" --> Prefix is: \" + usePrefix);", + " ", + " // Set up all globals, using the correct prefix.", + " pm.globals.set('auth_url', pm.globals.get(usePrefix + 'auth_url'));", + " pm.globals.set('realm', pm.globals.get(usePrefix + 'realm'));", + " pm.globals.set('clientid', pm.globals.get(usePrefix + 'clientid'));", + " pm.globals.set('client_secret', pm.globals.get(usePrefix + 'client_secret'));", + " pm.globals.set('url', pm.globals.get(usePrefix + 'url'));", + "}", + "else {", + " console.log(\"==> use-prefix does not exist\");", + " console.log(\" --> No default globals set.\");", + "}", + "", + "// If no maximum load time defined, set a default.", + "if (!pm.globals.get('max_load_time')) {", + " console.log(\"==> max_load_time not present, default set.\");", + " pm.globals.set(\"max_load_time\", JSON.stringify(1503));", + "}", + "", + "// If no maximum response defined, set a default.", + "if (!pm.globals.get('max_response_time')) {", + " console.log(\"==> max_response_time not present, default set.\");", + " pm.globals.set(\"max_response_time\", JSON.stringify(15005));", + "}", + "", + "// Display the values of all globals.", + "console.log(\"\");", + "console.log(\"==> Globals are:\");", + "console.log(\" --> auth_url: \" + pm.globals.get(\"auth_url\"));", + "console.log(\" --> realm: \" + pm.globals.get(\"realm\"));", + "console.log(\" --> clientid: \" + pm.globals.get(\"clientid\"));", + "console.log(\" --> client_secret: \" + pm.globals.get(\"client_secret\"));", + "console.log(\" --> url: \" + pm.globals.get(\"url\"));", + "console.log(\" --> max_load_time: \" + pm.globals.get(\"max_load_time\"));", + "console.log(\" --> max_response_time: \" + pm.globals.get(\"max_response_time\"));", + "", + "", + "// Clear run-scoped variables so reruns do not inherit stale state.", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.unset(\"operator_token\");", + "pm.globals.unset(\"operator_refresh_token\");", + "pm.globals.unset(\"nonqtxn_token\");", + "pm.globals.unset(\"nonqtxn_refresh_token\");", + "pm.globals.unset(\"public_user_token\");", + "pm.globals.unset(\"public_user_refresh_token\");", + "pm.globals.unset(\"token\");", + "pm.globals.unset(\"refresh_token\");", + "pm.globals.unset(\"token_expires\");", + "pm.globals.unset(\"refresh_token_expires\");", + "pm.globals.unset(\"expires_in\");", + "pm.globals.unset(\"refresh_expires_in\");", + "pm.environment.unset(\"current_client\");", + "pm.environment.unset(\"first_sr_id\");", + "pm.environment.unset(\"second_sr_id\");", + "pm.environment.unset(\"current_csr_id\");", + "pm.environment.unset(\"current_office_id\");", + "pm.environment.unset(\"current_office_number\");", + "pm.environment.unset(\"counter_id\");", + "pm.environment.unset(\"qtxn_id\");", + "pm.environment.unset(\"channel_telephone_id\");", + "pm.environment.unset(\"channel_email_id\");", + "pm.environment.unset(\"service_PropTax_id\");", + "pm.environment.unset(\"service_MSP_id\");", + "pm.environment.unset(\"user_id\");", + "pm.environment.unset(\"user_phone\");", + "pm.environment.unset(\"appointment_id\");", + "pm.environment.unset(\"public_office_id\");", + "pm.environment.unset(\"public_office_timezone\");", + "pm.environment.unset(\"public_service_id\");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Dummy data." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Basic-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a0d8e240-3b40-4654-a32b-bf2e676fdeb9", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"basic_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Complex-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "f006b951-3205-4f21-a315-369f85591929", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"complex_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response code for request is 200\", function(){\n pm.expect(pm.response.code).to.eql(200);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 200) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 200 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Create-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "382b7b65-d736-4a03-a44a-3891f33b617d", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"create_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Get the maximum response time allowed.\nmax_response_time = JSON.parse(globals.max_response_time);\n\npm.test('Response time less than ' + max_response_time.toString() + 'ms', function(){\n pm.expect(pm.response.responseTime).to.be.below(max_response_time);\n});\n\nvar contentType = pm.response.headers.get(\"content-type\") || \"\";\n\npm.test(\"Response status code should be 201 CREATED\", function(){\n pm.expect(pm.response.code).to.eql(201);\n});\npm.test(\"Response header should include Content-Type application/json\", function() {\n pm.expect(contentType.toLowerCase()).to.include(\"application/json\");\n});\npm.test(\"Response body should be valid JSON\", function() {\n pm.expect(function () { pm.response.json(); }).to.not.throw();\n});\n\nif (pm.response.code !== 201) {\n console.log(\"Unexpected status for \" + pm.info.requestName + \": \" + pm.response.code);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected HTTP 201 but received \" + pm.response.code);\n}\n\nif (contentType.toLowerCase().indexOf(\"application/json\") === -1) {\n console.log(\"Unexpected content-type for \" + pm.info.requestName + \": \" + contentType);\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw new Error(\"Expected application/json response\");\n}\n\ntry {\n pm.response.json();\n} catch (parseErr) {\n console.log(\"Response body was not valid JSON for \" + pm.info.requestName + \".\");\n console.log(responseBody);\n pm.execution.setNextRequest(null);\n throw parseErr;\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Citizen-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "8cebcf78-f5a2-4694-b108-60e146791a78", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"citizen_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var schema = {\n \"properties\" : {\n \"start_time\" : {\n \"type\" : \"string\"\n },\n \"citizen_name\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"number\", \"object\"]\n },\n \"qt_xn_citizen_ind\" : {\n \"type\" : \"number\"\n },\n \"ticket_number\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \"service_reqs\" : {\n \"type\" : \"array\"\n },\n \"office_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"cs\" : {\n \"type\" : \"object\"\n },\n \"citizen_comments\" : {\n \"type\" : [\"string\", \"null\"]\n },\n \n },\n \"required\" : [\"start_time\", \"citizen_name\", \"citizen_id\",\n \"qt_xn_citizen_ind\", \"ticket_number\", \"service_reqs\",\n \"office_id\", \"cs\", \"citizen_comments\"]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"citizens\")) {\n\tallElements = jsonData.citizens;\n};\n\nif (jsonData.hasOwnProperty(\"citizen\")) {\n\tallElements = [];\n\tallElements.push(jsonData.citizen);\n};\n\nvar elementCount = 0;\n\n// If there are some citizens, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each channel, create list of citizen ids.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Citizen (\" + elementCount + \"): \" + element.citizen_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n \n // Test the authenticate response.\n pm.test(testTitle + \"qt_xn_citizen_ind must be 0 or 1\", function() {\n pm.expect(element.qt_xn_citizen_ind).to.be.within(0,1);\n });\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Service-Response-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "0e6c4264-28b3-4a49-b6bb-1199aef2cb13", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"service_response_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "var schema = {\n \"properties\" : {\n \"sr_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"sr_state\" : {\n \"type\" : \"object\"\n },\n \"periods\" : {\n \"type\" : \"array\"\n },\n \"service\" : {\n \"type\" : \"object\"\n },\n \"citizen\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"quantity\" : {\n \"type\" : \"number\"\n },\n \"service_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"citizen_id\" : {\n \"type\" : [\"object\", \"number\"]\n },\n \"channel\" : {\n \t\"type\" : \"object\"\n },\n \"channel_id\" : {\n \t\"type\" : [\"object\", \"number\"]\n }\n \n },\n \"required\" : [\n \t\"sr_id\", \"sr_state\", \"periods\", \"service\", \"citizen\", \"quantity\",\n \t\"service_id\", \"citizen_id\", \"channel\", \"channel_id\"\n ]\n};\n\n// Declare, initialize variables.\nvar allElements = null;\n\nif (jsonData.hasOwnProperty(\"service_requests\")) {\n\tallElements = jsonData.service_requests;\n};\n\nif (jsonData.hasOwnProperty(\"service_request\")) {\n allElements = [];\n\tallElements.push(jsonData.service_request);\n}\n\nvar elementCount = 0;\n\n// If there are some service requests, proceed with tests.\nif (allElements !== null) {\n\n // Loop to validate schema of each service request.\n allElements.forEach(function(element) {\n elementCount ++;\n var testTitle = \"Service Request (\" + elementCount + \"): \" + element.sr_id + \" - \";\n tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);\n });\n};" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + }, + { + "name": "CFMS-Install-Get-Active-Citizens-Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "8856712d-73ca-4172-89ba-590e8b015c6b", + "exec": [ + "var jsonData = pm.response.json();", + "pm.environment.set(\"get_active_citizens_test\", jsonData.data);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "// Declare and initialize variables.\nvar elementCount = 0;\nvar srCount = 0;\nvar isFirstCitizen = true;\n\n\n// Loop to create list of active citizen ids.\nallElements.forEach(function(element) {\n srCount = element.service_reqs.length;\n\n // If citizen active, add to the list.\n if (element.cs.cs_state_name === \"Active\") {\n //console.log(\"Citizen (\" + elementCount + \") \" + element.citizen_id +\n // \" Active: SRCount = \" + srCount);\n citizenIds.push(element.citizen_id);\n\n // Save the first citizen.\n if (isFirstCitizen) {\n currentCitizen = element;\n isFirstCitizen = false;\n }\n }\n \n // Increment count.\n elementCount++;\n});" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "Sets the authentication script" + }, + "response": [] + } + ], + "description": "This folder performs basic authentication features.", + "protocolProfileBehavior": {} + }, + { + "name": "Check app health", + "item": [ + { + "name": "Check healthz driver TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum response time allowed.", + "max_load_time = JSON.parse(globals.max_load_time);", + "", + "// Set health response time variable.", + "health_tries = 15;", + "counter = 1;", + "postman.setEnvironmentVariable(\"health_tries\", JSON.stringify(health_tries));", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long. Try again, give pod a chance to spin up.", + "else {", + " postman.setNextRequest(\"Check the healthz endpoint TheQ\");", + "}" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the healthz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "74adc5ce-caa5-452a-bd96-8ac90a4c11d7", + "exec": [ + "// Get the maximum load time allowed.", + "max_load_time = JSON.parse(postman.getEnvironmentVariable(\"max_load_time\"));", + "", + "// Get and update variables.", + "health_tries = JSON.parse(postman.getEnvironmentVariable(\"health_tries\"));", + "counter = JSON.parse(postman.getEnvironmentVariable(\"health_counter\")) + 1;", + "postman.setEnvironmentVariable(\"health_counter\", JSON.stringify(counter));", + "", + "// Get the response.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Health Driver: Try \" + counter.toString() + \": Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is healthy'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is healthy');", + "});", + "", + "// If response time is OK, proceed to the next test.", + "if (pm.response.responseTime < max_load_time) {", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + "}", + " ", + "// Response time is too long.", + "else {", + " ", + " // You haven't reached your maximum tries yet. Try again.", + " if (counter < health_tries) {", + " postman.setNextRequest(\"Check the healthz endpoint\");", + " }", + " ", + " // You have reached the maximum. An error, go to next test.", + " else {", + " pm.test(\"Response should be below \" + max_load_time.toString() + ' in ' + health_tries.toString() + ' tries.', function() {", + " pm.expect(counter).to.be.below(health_tries);", + " });", + " postman.setNextRequest(\"Check the readyz endpoint TheQ\");", + " }", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}healthz/", + "host": [ + "{{url}}healthz" + ], + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Check the readyz endpoint TheQ", + "event": [ + { + "listen": "test", + "script": { + "id": "661c33c4-4a3d-4396-a549-9969edafbfa6", + "exec": [ + "// Perform the standard tests.", + "eval(environment.basic_response_test);", + "", + "var jsonData = JSON.parse(responseBody);", + "", + "// Test the health response.", + "pm.test(\"Response should have 'message' property\", function() {", + " pm.expect(jsonData).to.have.property('message');", + "});", + "", + "pm.test(\"Response message should be 'api is ready'\", function() {", + " pm.expect(jsonData.message).to.be.eql('api is ready');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "9e00b481-ef9a-440b-8e83-025fc49026c4", + "exec": [ + "(function () {", + " var requiredVars = [\"url\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}readyz/", + "host": [ + "{{url}}readyz" + ], + "path": [ + "" + ] + } + }, + "response": [] + } + ], + "description": "Checks the application health by calling the healthz and readyz endpoints", + "protocolProfileBehavior": {} + }, + { + "name": "Check user login", + "item": [ + { + "name": "Authenticate default QTxn user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "(function () {", + " var requiredVars = [\"auth_url\",\"realm\",\"clientid\",\"userid\",\"password\",\"client_secret\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "43df8e47-2b93-48b0-a5ff-bb3396d12537", + "exec": [ + "// Parse response body", + "var jsonData = {};", + "try {", + " jsonData = JSON.parse(responseBody);", + "} catch (parseErr) {", + " console.log(\"Authentication response body was not JSON.\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", \"Non-JSON auth response\");", + " pm.execution.setNextRequest(null);", + " throw parseErr;", + "}", + "", + "var authFailureReason = jsonData.error_description || jsonData.error || (\"HTTP \" + pm.response.code);", + "if (pm.response.code !== 200 || !jsonData.access_token) {", + " console.log(\"Authentication failed for \" + pm.info.requestName + \".\");", + " console.log(responseBody);", + " pm.globals.set(\"auth_failure_reason\", authFailureReason);", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Authentication failed: \" + authFailureReason);", + "}", + "", + "pm.test(\"Response code for request is 200\", function(){", + " pm.expect(pm.response.code).to.eql(200);", + "});", + "pm.test(\"Access token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"access_token\");", + " pm.expect(jsonData.access_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Refresh token exists\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_token\");", + " pm.expect(jsonData.refresh_token).to.be.a(\"string\").and.not.empty;", + "});", + "pm.test(\"Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"expires_in\");", + " pm.expect(jsonData.expires_in).to.not.eql(null);", + "});", + "pm.test(\"Refresh Expires In is present\", function(){", + " pm.expect(jsonData).to.have.property(\"refresh_expires_in\");", + " pm.expect(jsonData.refresh_expires_in).to.not.eql(null);", + "});", + "", + "pm.globals.unset(\"auth_failure_reason\");", + "pm.globals.set(\"operator_token\", jsonData.access_token);", + "pm.globals.set(\"operator_refresh_token\", jsonData.refresh_token);", + "pm.globals.set(\"token_expires\", Date.now() + (jsonData.expires_in * 1000));", + "pm.globals.set(\"refresh_token_expires\", Date.now() + (jsonData.refresh_expires_in * 1000));", + "pm.globals.set(\"expires_in\", jsonData.expires_in);", + "pm.globals.set(\"refresh_expires_in\", jsonData.refresh_expires_in);", + "pm.globals.set(\"token\", jsonData.access_token);", + "pm.globals.set(\"refresh_token\", jsonData.refresh_token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "grant_type=password&client_id={{clientid}}&username={{userid}}&password={{password}}&client_secret={{client_secret}}" + }, + "url": { + "raw": "{{auth_url}}/auth/realms/{{realm}}/protocol/openid-connect/token?Content-Type=application/x-www-form-urlencoded", + "host": [ + "{{auth_url}}" + ], + "path": [ + "auth", + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ], + "query": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "description": "Make sure the operator ID can log in" + }, + "response": [] + }, + { + "name": "Who am I TheQ", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + "\tcurrentCsrId = jsonData.csr.csr_id;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", currentCsrId);", + "};", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check channels", + "item": [ + { + "name": "Get channels", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "2b71673c-4aa2-47b3-8594-15f02b390ee0", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "42742c4c-505d-472d-857b-fdba74cc49ac", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"channel_name\" : {", + " \"type\" : \"string\"", + " },", + " \"channel_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " }", + " },", + " \"required\" : [\"channel_name\", \"channel_id\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allChannels = jsonData.channels;", + "var channelCount = 0;", + "var phoneId = 0;", + "var emailId = 0;", + "var phoneText = \"Phone\";", + "var emailText = \"Email/Fax/Mail\";", + "allChannels.forEach(function(channel) {", + " channelCount ++;", + " var testTitle = \"Channel (\" + channelCount + \"): ID \" + channel.channel_id + \" Name \" + channel.channel_name + \" conforms to schema\";", + " tests[testTitle] = tv4.validate(channel, schema);", + " if (channel.channel_name === phoneText) {", + " phoneId = channel.channel_id;", + " }", + " if (channel.channel_name === emailText) {", + " emailId = channel.channel_id;", + " }", + "});", + "", + "// Check that you found the phone ID.", + "pm.test(phoneText + ' id was ' + phoneId.toString() + ' (should not equal 0)', function() {", + " pm.expect(phoneId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the email ID.", + "pm.test(emailText + ' id was ' + emailId.toString() + ' (should not equal 0)', function() {", + " pm.expect(emailId).to.not.be.eql(0);", + "});", + "", + "// Store this ID for future use.", + "postman.setEnvironmentVariable(\"channel_telephone_id\", JSON.stringify(phoneId));", + "postman.setEnvironmentVariable(\"channel_email_id\", JSON.stringify(emailId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}channels/", + "host": [ + "{{url}}channels" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check counters", + "item": [ + { + "name": "Store CSR and Office Info", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "c34723d3-c0d1-4504-97a2-373088309a5b", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77f0ca3-62cf-49ba-8e17-67a16e8f8a5f", + "exec": [ + "// Define the JSON Schema expected in response", + "var CSRSSchema = {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_id\": {\"type\": \"number\"},", + " \"csr_state\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"csr_state_desc\": {\"type\": \"string\"},", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"csr_state_name\": {\"type\": \"string\"}", + " },", + " \"required\": [\"csr_state_desc\", \"csr_state_id\", \"csr_state_name\"]", + " },", + " \"csr_state_id\": {\"type\": \"number\"},", + " \"deleted\": {},", + " \"finance_designate\": {\"type\": \"number\"},", + " \"office_manager\": {\"type\": \"number\"},", + " \"ita2_designate\": {\"type\": \"number\"},", + " \"office\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"appointments_enabled_ind\": {\"type\": \"number\"},", + " \"exams_enabled_ind\": {\"type\": \"number\"},", + " \"office_id\": {\"type\": \"number\"},", + " \"office_name\": {\"type\": \"string\"},", + " \"office_number\": {\"type\": \"number\"},", + " \"sb\":{", + " \"type\": \"object\",", + " \"properties\": {", + " \"sb_id\": {\"type\": \"number\"},", + " \"sb_type\": {\"type\": \"string\"}", + " },", + " \"required\": [\"sb_id\", \"sb_type\"]", + " }", + " },", + " \"required\": [\"appointments_enabled_ind\", \"exams_enabled_ind\", \"office_id\", \"office_name\", \"office_number\", \"sb\"]", + " },", + " \"office_name\": {\"type\": \"string\"},", + " \"pesticide_designate\": {\"type\": \"number\"},", + " \"qt_xn_csr_ind\": {\"type\": \"number\"},", + " \"receptionist_ind\": {\"type\": \"number\"},", + " \"role\": {", + " \"type\": \"object\",", + " \"properties\": {", + " \"role_code\": {\"type\": \"string\"},", + " \"role_desc\": {\"type\": \"string\"},", + " \"role_id\": {\"type\": \"number\"}", + " },", + " \"required\": [\"role_code\", \"role_desc\", \"role_id\"]", + " },", + " \"role_id\": {\"type\": \"number\"},", + " \"username\": {\"type\": \"string\"}", + " },", + " \"attention_needed\": {\"type\": \"boolean\"},", + " \"required\": [\"csr\", \"attention_needed\"]", + "};", + "", + "// Run basic response tests.", + "eval(environment.basic_response_test);", + "", + "// Parse response body", + "var jsonData = JSON.parse(responseBody);", + "", + "//Test to see if response schema is valid", + "pm.test(\"Validate Response CSRs Schema\", function(){", + " pm.expect(tv4.validate(jsonData, CSRSSchema)).to.be.true;", + "});", + "", + "// Make sure that jsonData has an csr property.", + "pm.test(\"Response should have csr property\", function(){", + " pm.expect(jsonData.hasOwnProperty(\"csr\")).to.be.true;", + "});", + "", + "var csr = 0;", + "var office = 0;", + "var counters = 0;", + "var counter_text = \"Counter\";", + "var counter_id = 0;", + "var qtxn_text = \"Quick Trans\";", + "var qtxn_id = 0;", + "", + "if (jsonData.hasOwnProperty(\"csr\")) {", + "\tcurrentOfficeId = jsonData.csr.office_id;", + "\tcurrentOfficeNumber = jsonData.csr.office.office_number;", + " postman.setEnvironmentVariable(\"current_office_id\", currentOfficeId);", + " postman.setEnvironmentVariable(\"current_office_number\", currentOfficeNumber);", + " postman.setEnvironmentVariable(\"current_csr_id\", jsonData.csr.csr_id);", + " ", + " csr = jsonData.csr;", + " counter_id = 0;", + " qtxn_id = 0;", + "", + " // Make sure that jsonData has an booking property.", + " pm.test(\"CSR should have office property\", function(){", + " pm.expect(csr.hasOwnProperty(\"office\")).to.be.true;", + " });", + " ", + " // Make sure office has counter property.", + " if (csr.hasOwnProperty(\"office\")) {", + " office = csr.office;", + " ", + " // Make sure that jsonData has an booking property.", + " pm.test(\"Office should have counters property\", function(){", + " pm.expect(office.hasOwnProperty(\"counters\")).to.be.true;", + " });", + "", + " // Make sure office has counter property.", + " if (office.hasOwnProperty(\"counters\")) {", + " counters = office.counters;", + " ", + " // Search for Counter and Quick Trans counters", + " counters.forEach(function(counter) {", + " if (counter.counter_name === counter_text) {", + " counter_id = counter.counter_id;", + " }", + " if (counter.counter_name === qtxn_text) {", + " qtxn_id = counter.counter_id;", + " }", + " });", + " ", + " // Make sure you found the right IDs.", + " pm.test(\"Counter ID (\" + counter_id.toString() + \") should not be 0\", function(){", + " pm.expect(counter_id).to.not.be.eql(0);", + " });", + " pm.test(\"Quick Transaction ID (\" + qtxn_id.toString() + \") should not be 0\", function(){", + " pm.expect(qtxn_id).to.not.be.eql(0);", + " });", + " ", + " // Store the ids for future use.", + " // postman.setEnvironmentVariable(\"counter_id\", counter_id);", + " // postman.setEnvironmentVariable(\"qtxn_id\", qtxn_id);", + " postman.setEnvironmentVariable(\"counter_id\", JSON.stringify(counter_id.toString()));", + " postman.setEnvironmentVariable(\"qtxn_id\", JSON.stringify(qtxn_id.toString()));", + " }", + "", + " ", + " }", + "", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}csrs/me/", + "host": [ + "{{url}}csrs" + ], + "path": [ + "me", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check categories", + "item": [ + { + "name": "Get categories", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "e99f23ee-5a03-43d9-9075-75dbf06aadcc", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1a9c9ff5-8475-4dd2-a6c7-ffb5d4689145", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allCategories = jsonData.categories;", + "var categoryCount = 0;", + "allCategories.forEach(function(category) {", + " categoryCount ++;", + " var testTitle = \"Category (\" + categoryCount + \"): \" + category.service_name + \" - \";", + " tests[testTitle + \"conforms to schema\"] = tv4.validate(category, schema);", + " var displayInd = category.display_dashboard_ind;", + " var serviceInd = category.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.eql(0);", + " });", + "", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 0 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(0);", + " });", + " ", + " pm.test(\"--> \" + testTitle + \"parent_id must be null\", function(){", + " pm.expect(category.parent_id).to.be.null;", + " });", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}categories/", + "host": [ + "{{url}}categories" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check services", + "item": [ + { + "name": "Get services", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "d01c3826-dc28-4cce-957b-ec70d0393e7e", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3b11031e-4031-4569-91f2-49e23517ffee", + "exec": [ + "// Run basic tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "var schema = {", + " \"properties\" : {", + " \"display_dashboard_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"deleted\" : {", + " \"type\" : [\"object\", \"null\"]", + " },", + " \"actual_service_ind\" : {", + " \"type\" : \"number\"", + " },", + " \"service_id\" : {", + " \"type\" : [\"number\", \"object\"]", + " },", + " \"service_code\" : {", + " \"type\" : \"string\"", + " },", + " \"prefix\" : {", + " \"type\" : \"string\"", + " },", + " \"service_name\" : {", + " \"type\" : \"string\"", + " },", + " \"parent_id\" : {", + " \"type\" : [\"object\", \"number\", \"null\" ]", + " },", + " \"service_desc\" : {", + " \"type\" : \"string\"", + " }", + " },", + " \"required\" : [\"display_dashboard_ind\", \"deleted\", \"actual_service_ind\", \"service_id\", \"service_code\",", + " \"prefix\", \"service_name\", \"parent_id\", \"service_desc\"]", + "};", + "", + "// Loop to validate schema of each channel.", + "var allElements = jsonData.services;", + "var elementCount = 0;", + "var elementMax = Math.min(10, allElements.length);", + "//allElements.forEach(function(element) {", + "for (var currentElement = 0; currentElement < elementMax; currentElement++) {", + " element = allElements[currentElement];", + " elementCount ++;", + " var testTitle = \"Service (\" + elementCount + \"): \" + element.service_name + \" - \";", + " tests[testTitle + \"conforms to schema\"] = tv4.validate(element, schema);", + " displayInd = element.display_dashboard_ind;", + " serviceInd = element.actual_service_ind;", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"display_dashboard_ind must be 0 or 1 (is \" + displayInd.toString() + \")\", function(){", + " pm.expect(displayInd).to.be.within(0, 1);", + " });", + "", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"actual_service_ind must be 1 (is \" + serviceInd.toString() + \")\", function(){", + " pm.expect(serviceInd).to.be.eql(1);", + " });", + " ", + " // Test that returned data is valid.", + " pm.test(\"--> \" + testTitle + \"parent_id must not be null\", function(){", + " pm.expect(element.parent_id).to.not.be.null;", + " });", + "}", + "", + "// Declare and initialize variables.", + "var mspId = 0;", + "var taxId = 0;", + "var mspText = \"Payment - MSP\";", + "var propTaxText = \"Other - PTAX\";", + "", + "// Look for the MSP and Property Tax IDs.", + "allElements.forEach(function(element) {", + " if (element.service_name === mspText) {", + " mspId = element.service_id;", + " }", + " if (element.service_name === propTaxText) {", + " taxId = element.service_id;", + " }", + "});", + "", + "// Check that you found the MSP service.", + "pm.test(mspText + ' id was ' + mspId.toString() + ' (should not equal 0)', function() {", + " pm.expect(mspId).to.not.be.eql(0);", + "});", + "", + "// Check that you found the Property Tax service.", + "pm.test(propTaxText + ' id was ' + taxId.toString() + ' (should not equal 0)', function() {", + " pm.expect(taxId).to.not.be.eql(0);", + "});", + "", + "// Store these IDs for future use.", + "postman.setEnvironmentVariable(\"service_MSP_id\", JSON.stringify(mspId));", + "postman.setEnvironmentVariable(\"service_PropTax_id\", JSON.stringify(taxId));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + } + ], + "url": { + "raw": "{{url}}services/", + "host": [ + "{{url}}services" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check citizen through queue (QT1)", + "item": [ + { + "name": "Create citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "7896261d-7d88-4eba-860b-98472655c4a7", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "b72e7756-5de1-4a13-9609-8917ea63dee7", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "73110fdd-6476-403d-b6c9-c108032fbd66", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "66ca9683-be00-478b-92c0-6ac698d9088b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8a294876-edfa-4d5b-ada4-399f5339cd8f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Invite specific citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8e9cd1ef-a5d9-4f81-b061-f818d4fc5603", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Invited\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Invited\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/invite/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "invite", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1b1f7f66-04cd-4f10-bfa2-c4f3fffe8715", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT1)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "818713b9-18a0-4fef-9c16-f71b43dd2ad0", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check citizen begin-hold-finish (QT2)", + "item": [ + { + "name": "Create citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e77d15de-91e2-4528-87bd-5734405b4def", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " ", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "} ", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "ce732be5-38a3-4825-90d1-4d52babf8917", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "13828662-748d-4b3f-9ead-454e795f1f01", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2a57d1b2-9d0c-4e07-ad20-d4dde457f9d1", + "exec": [ + "// Install postmanBDD, json-bigint parse and stringify.", + "eval(globals.postmanBDD);", + "eval(globals.json_bigint_parse);", + "", + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "4550a6b7-560d-48bf-bfc9-41453a8defec", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Place citizen on hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "dd51a48d-2e1e-47fd-978f-507b77a7901f", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 3 (now three periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/place_on_hold/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "place_on_hold", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Get service requests (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "f4809d17-b59d-47fc-861e-5edaf421bddc", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + "var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + "var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get periods for the first service request.", + " var allPeriods = allElements[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " var allPeriodCount = 0;", + "", + " // Find how many periods there are with null end time.", + " // Also, check schema.", + " allPeriods.forEach(function(onePeriod) {", + " ", + " // Find the open period.", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " ", + " });", + "} ", + "", + "// If there are some service requests, proceed with tests.", + "if (allElements !== null) {", + "", + " // Perform tests.", + " pm.test('There must be only one service request', function() {", + " pm.expect(allElements.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(allElements[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have three periods', function() {", + " pm.expect(allPeriods.length).to.be.eql(3);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('Service request period state must be \"On hold\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"On hold\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/service_requests/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "service_requests", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Call citizen from hold (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "66563797-2a92-4dbd-946e-7232dcac8260", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (now four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT2)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ec865f9c-a4b6-421f-956e-783972df2ad3", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = allElements[0].service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 4 (still four periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(4);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check citizen leave after create (QT3)", + "item": [ + { + "name": "Create citizen (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "061a479c-37b4-4707-b8c6-c31cacadecec", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT3)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "16eaefc2-0d16-405e-9e70-e85314da841b", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + " var citizenComment = postman.getEnvironmentVariable(\"citizen_comment\");", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = null;", + " if (currentCitizen.service_reqs.length !== 0) {", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " }", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " if (allPeriods !== null) {", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + " }", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check citizen leave after waiting (QT4)", + "item": [ + { + "name": "Create citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fcc1415c-0861-4516-8843-bfc3717a76d9", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "f26eaedb-5d41-4dcc-b856-aec599440601", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// }", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "e0b48324-0ca2-4b2e-88ef-cbfbe10b5fb7", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "1176fcca-52ff-4e13-b4a4-775ad324a4c6", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add citizen to queue (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "f0aacc69-ff0e-4ed3-9ef7-f131bbf3a5bd", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Pending\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Pending\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Waiting\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Waiting\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/add_to_queue/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "add_to_queue", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Citizen left (QT4)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "4ee90115-54c2-4b44-b6f1-d739e8a385b1", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test(\"Must be no active citizens in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test(\"Citizen should have one service request\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/citizen_left/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "citizen_left", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Check update service information (QT5)", + "item": [ + { + "name": "Create citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "d3bbd88a-25fd-4fe6-ae7e-359373df02ac", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + "", + " // Perform tests.", + " pm.test(\"Only one citizen should be in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test(\"Current citizen name should be null\", function() {", + " pm.expect(currentCitizen.citizen_name).to.be.null;", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "", + " // Store the ID of the citizen just created.", + " postman.setEnvironmentVariable(\"current_client\", JSON.stringify(citizenIds.shift()));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}" + }, + "url": { + "raw": "{{url}}citizens/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Edit specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "2be395b2-e14c-4ff8-8753-42ea1fe2010d", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\",\"citizen_name\",\"citizen_comment\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in.", + "// // Get data, create JSON body.", + "// var citizenName = postman.getEnvironmentVariable(\"citizen_name\");", + "// var citizenComments = postman.getEnvironmentVariable(\"citizen_comment\");", + "// var bodyData = {", + "// \"citizen_name\" : citizenName,", + "// \"citizen_comments\" : citizenComments", + "// };", + "// // Store the data in an environment variable.", + "// postman.setEnvironmentVariable(\"putBody\", JSON.stringify(bodyData));" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "2e7878d3-653e-479e-850f-c454b378217e", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + "", + " // Perform tests.", + " pm.test(\"Must be one active citizen in the office\", function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test(\"Citizen should have no service requests\", function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"citizen_name\" : {{citizen_name}},\n \"citizen_comments\" : {{citizen_comment}}\n}" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Add property tax via phone service request (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"operator_token\",\"service_PropTax_id\",\"current_client\",\"citizen_quantity\",\"channel_telephone_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "fa6f6a3b-e4e7-42b6-a8d3-ecda3840418a", + "exec": [ + "// Run complex tests.", + "eval(environment.create_response_test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_request\" : {\n\t\t\"service_id\" : {{service_PropTax_id}},\n\t\t\"citizen_id\" : {{current_client}},\n\t\t\"quantity\" : {{citizen_quantity}},\n\t\t\"channel_id\" : {{channel_telephone_id}}\n\t}\n}" + }, + "url": { + "raw": "{{url}}service_requests/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "List specific citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "3d906a10-51fe-4329-bac0-e69e6847e852", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + " ", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request must have one period', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods.length).to.be.eql(1);", + " });", + " pm.test('Service request period channel must be ' + citizenChannel, function() {", + " pm.expect(currentCitizen.service_reqs[0].channel_id).to.be.eql(citizenChannel);", + " });", + " pm.test('Service request period state must be \"Ticket Creation\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].periods[0].ps.ps_name).to.be.eql(\"Ticket Creation\");", + " });", + "", + " // Save the service request ID for later.", + " var mySRId = allElements[0].service_reqs[0].sr_id;", + " postman.setEnvironmentVariable(\"current_sr_id\", JSON.stringify(mySRId));", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{url}}citizens/{{current_client}}/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Begin serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "ff3a25ca-0df2-40f2-b7a1-9e9b4bde2aab", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = null;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Run citizen tests.", + " eval(environment.get_active_citizens_test);", + " ", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_PropTax_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be one active citizen in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(1);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('Citizen comment must equal \"' + citizenComment + '\"', function() {", + " pm.expect(currentCitizen.citizen_comments).to.be.eql(citizenComment);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Active\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Active\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (now two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must only be one open period', function() {", + " pm.expect(openPeriodCount).to.be.eql(1);", + " });", + " pm.test('The open period state must be \"Being Served\"', function() {", + " pm.expect(openPeriod.ps.ps_name).to.be.eql(\"Being Served\");", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "", + "value": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/begin_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "begin_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update quantity from 3 to 5 (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"citizen_quantity_update\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "153a139d-0daa-4031-ba0a-e7204ade2648", + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + "", + " // Perform tests.", + " pm.test('Updated service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(allElements[0].quantity).to.be.eql(citizenQuantity);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"quantity\" : {{citizen_quantity_update}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Update service from PropTax to MSP (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_sr_id\",\"operator_token\",\"service_MSP_id\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8faaa693-a7a4-45ed-9318-325a498a98af", + "exec": [ + "// Run complex tests.", + "eval(environment.basic_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.service_response_test);", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Only check for an updated quantity. Get environment variables.", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + "", + " // Perform tests.", + " pm.test('Updated service request service must be ' + citizenService, function() {", + " pm.expect(allElements[0].service_id).to.be.eql(citizenService);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"service_id\" : {{service_MSP_id}}\n}" + }, + "url": { + "raw": "{{url}}service_requests/{{current_sr_id}}/", + "host": [ + "{{url}}service_requests" + ], + "path": [ + "{{current_sr_id}}", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + }, + { + "name": "Finish serving citizen (QT5)", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "becda71c-71d6-4d2f-bad5-1a7a17da6128", + "exec": [ + "(function () {", + " var requiredVars = [\"url\",\"current_client\",\"operator_token\"];", + " var missingVars = requiredVars.filter(function (name) {", + " var value = pm.variables.get(name);", + " return value === undefined || value === null || value === \"\";", + " });", + " if (missingVars.length) {", + " console.log(\"Missing prerequisite variables for \" + pm.info.requestName + \": \" + missingVars.join(\", \"));", + " pm.execution.setNextRequest(null);", + " throw new Error(\"Missing prerequisite variables: \" + missingVars.join(\", \"));", + " }", + "})();", + "// Ensure the client is logged in." + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "7a831dcf-ff2c-4907-95c5-bf042cdd967a", + "exec": [ + "// Run complex tests.", + "eval(environment.complex_response_test);", + "", + "// Get json return data.", + "var jsonData = JSON.parse(responseBody);", + "", + "// Run citizen tests.", + "eval(environment.citizen_response_test);", + "", + "// Declare, initialize variables.", + "var citizenIds = [];", + "var currentCitizen = jsonData.citizen;", + "", + "// If there are some citizens, proceed with tests.", + "if (allElements !== null) {", + "", + " // Get environment variables.", + " var citizenName = JSON.parse(postman.getEnvironmentVariable(\"citizen_name\"));", + " var citizenComment = JSON.parse(postman.getEnvironmentVariable(\"citizen_comment\"));", + " var citizenService = JSON.parse(postman.getEnvironmentVariable(\"service_MSP_id\"));", + " var citizenQuantity = JSON.parse(postman.getEnvironmentVariable(\"citizen_quantity_update\"));", + " var citizenChannel = JSON.parse(postman.getEnvironmentVariable(\"channel_telephone_id\"));", + " var currentCitizenId = JSON.parse(postman.getEnvironmentVariable(\"current_client\"));", + " var allPeriods = currentCitizen.service_reqs[0].periods;", + " var openPeriod = null;", + " var openPeriodCount = 0;", + " ", + " // Find how many periods there are with null end time.", + " allPeriods.forEach(function(onePeriod) {", + " if (!onePeriod.time_end) {", + " openPeriod = onePeriod;", + " openPeriodCount++;", + " }", + " });", + "", + " // Perform tests.", + " pm.test('Must be no active citizens in the office', function() {", + " pm.expect(citizenIds.length).to.be.eql(0);", + " });", + " pm.test('Citizen Id must equal \"' + currentCitizenId + '\"', function() {", + " pm.expect(currentCitizen.citizen_id).to.be.eql(currentCitizenId);", + " });", + " pm.test('Citizen name must equal \"' + citizenName + '\"', function() {", + " pm.expect(currentCitizen.citizen_name).to.be.eql(citizenName);", + " });", + " pm.test('There must be only one service request', function() {", + " pm.expect(currentCitizen.service_reqs.length).to.be.eql(1);", + " });", + " pm.test('Service request state must be \"Complete\"', function() {", + " pm.expect(currentCitizen.service_reqs[0].sr_state.sr_code).to.be.eql(\"Complete\");", + " });", + " pm.test('Service request service must be ' + citizenService, function() {", + " pm.expect(currentCitizen.service_reqs[0].service_id).to.be.eql(citizenService);", + " });", + " pm.test('Service request quantity must be ' + citizenQuantity, function() {", + " pm.expect(currentCitizen.service_reqs[0].quantity).to.be.eql(citizenQuantity);", + " });", + " pm.test('Service request periods length must be 2 (should be two periods)', function() {", + " pm.expect(allPeriods.length).to.be.eql(2);", + " });", + " pm.test('There must be no open periods', function() {", + " pm.expect(openPeriodCount).to.be.eql(0);", + " });", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{operator_token}}" + }, + { + "key": "Accept", + "value": "application/json, text/plain, */*" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{url}}citizens/{{current_client}}/finish_service/", + "host": [ + "{{url}}citizens" + ], + "path": [ + "{{current_client}}", + "finish_service", + "" + ] + }, + "description": "Get a list of clients in the queue" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} diff --git a/api/postman/README-local-auth.md b/api/postman/README-local-auth.md new file mode 100644 index 000000000..11c27f1dc --- /dev/null +++ b/api/postman/README-local-auth.md @@ -0,0 +1,55 @@ +# Local Newman Auth Notes + +Use the current local Keycloak realm and client when running the Postman collections with Newman: + +- Realm: `servicebc-local` +- Client ID: `theq-queue-management-api` +- Client secret: `theq-local-dev-secret` +- Auth base URL: `http://localhost:8085/auth` +- API base URL: `http://localhost:5000/api/v1/` + +The checked-in local realm now gives the Newman client both local service audiences: + +- `theq-queue-management-api` +- `theq-notifications-api` + +That matters because appointment creation forwards the caller bearer token to `notifications-api` for email/SMS side effects. Without the notifications audience on the Newman token, the booking request can still pass while the local API logs a downstream `401` from `http://localhost:5002`. + +Before running the collections, seed the local API database so the expected CSR records exist: + +```bash +cd /Users/csampson/Developer/Repositories/queue-management/api +uv run python manage.py db upgrade +uv run python manage.py bootstrap +``` + +The legacy `account` client and `registry` realm values in older examples are not valid for the checked-in local stack. + +The Postman users in `keycloak-local/servicebc-local-realm.json` need claims that match the current API token parsing: + +- Internal users need `theq_username` and `identity_provider=idir` +- Public users need `theq_username`, `identity_provider=bceid`, and `display_name` +- The public user also needs non-empty `email` and `lastName` so `/users/` schema checks pass + +If you already imported the local realm before pulling this change, re-import `keycloak-local/servicebc-local-realm.json` or add the `audience-theq-notifications-api` protocol mapper to client `theq-queue-management-api` manually. The Newman command does not change. + +The checked-in collections now authenticate explicitly during setup and fail fast if auth or a prerequisite variable is missing. For local and CI runs, prefer `--bail failure` so one auth/setup issue does not cascade into misleading `404`, `422`, or schema failures. + +Example Newman command: + +```bash +cd /Users/csampson/Developer/Repositories/queue-management/api/postman +./node_modules/newman/bin/newman.js run API_Test_TheQ_Booking.json -e postman_env.json --bail failure \ + --global-var userid=cfms-postman-operator \ + --global-var password=password \ + --global-var userid_nonqtxn=cfms-postman-non-operator \ + --global-var password_nonqtxn=password \ + --global-var client_secret=theq-local-dev-secret \ + --global-var url=http://localhost:5000/api/v1/ \ + --global-var auth_url=http://localhost:8085 \ + --global-var clientid=theq-queue-management-api \ + --global-var realm=servicebc-local \ + --global-var public_url=http://localhost:5000/api/v1/ \ + --global-var public_user_id=cfms-postman-public-user \ + --global-var public_user_password=password +``` diff --git a/api/postman/package.json b/api/postman/package.json new file mode 100644 index 000000000..49276a76f --- /dev/null +++ b/api/postman/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "newman": "^6.2.2" + } +} diff --git a/documentation/Readme.md b/documentation/Readme.md index f279daa26..53e3f3494 100644 --- a/documentation/Readme.md +++ b/documentation/Readme.md @@ -255,17 +255,32 @@ For tests to run, you require two additional IDs created in your keycloak: ## Postman Tests -Below is an example suing the localhost keycloak created above: +Below is an example using the checked-in localhost Keycloak realm from `compose.yaml` and `keycloak-local/servicebc-local-realm.json`: -- The application is now secured by roles. To add roels to the token, go to the client (id : account) and enable 'Full Scope Allowed' under Scope tab. - Create internal_user role and assign to anyone who will be accessing the application as a staff user - Create online_appointment_user role and assign to anyone who will be accessing the application as a public user - Create users & set passwords for the postman users in your keycloak instance: -1. cfms-postman-operator (role: internal_user) -1. cfms-postman-non-operator (role: internal_user) -2. cfms-postman-public-user (role: online_appointment_user, with an attribute displayName and map it as display_name in token) +1. cfms-postman-operator (role: internal_user, attributes `theq_username=cfms-postman-operator`, `identity_provider=idir`, `display_name=Postman Operator`) +1. cfms-postman-non-operator (role: internal_user, attributes `theq_username=cfms-postman-non-operator`, `identity_provider=idir`, `display_name=Postman Non Operator`) +1. cfms-postman-public-user (role: online_appointment_user, attributes `theq_username=cfms-postman-public-user`, `identity_provider=bceid`, `display_name=cfms-postman-public-user`, with non-empty `email` and `lastName`) + +Before running Newman, seed the local API database so the expected CSR records exist: + +1. `(cd api; uv run python manage.py db upgrade)` +1. `(cd api; uv run python manage.py bootstrap)` + +The older `account` client and `registry` realm examples are legacy and do not match the current local stack. + +The checked-in local realm now maps both audiences onto Newman tokens minted by client `theq-queue-management-api`: + +1. `theq-queue-management-api` +1. `theq-notifications-api` + +This keeps local appointment-booking Newman runs from logging a downstream `401` when the API forwards the caller token to `notifications-api` for email or SMS side effects. + +If you already imported the local realm before pulling this change, re-import `keycloak-local/servicebc-local-realm.json` or update client `theq-queue-management-api` in Keycloak to include protocol mapper `audience-theq-notifications-api`. Go \queue-manaement\api\postman & run the following command: @@ -275,20 +290,22 @@ You will need the following information: 1. password_qtxn= 1. password_nonqtxn= -1. client_secret=5abdcb03-9dc6-4789-8c1f-8230c7d7cb79 +1. client_secret=theq-local-dev-secret 1. url=http://localhost:5000/api/v1/ 1. auth_url=http://localhost:8085 -1. clientid=account -1. realm=registry +1. clientid=theq-queue-management-api +1. realm=servicebc-local 1. public_url=http://localhost:5000/api/v1/ 1. public_user_id=cfms-postman-public-user 1. public_user_password= -For this test, I created the password for the two users as demo. From the postman folder run the following command to run the postman tests: +For this test, the checked-in local realm uses `password` for the Postman users. From the postman folder run the following command to run the postman tests: -`./node_modules/newman/bin/newman.js run API_Test_TheQ_Booking.json -e postman_env.json --global-var userid=cfms-postman-operator --global-var password=demo --global-var userid_nonqtxn=cfms-postman-non-operator --global-var password_nonqtxn=demo --global-var client_secret=5abdcb03-9dc6-4789-8c1f-8230c7d7cb79 --global-var url=http://localhost:5000/api/v1/ --global-var auth_url=http://localhost:8085 --global-var clientid=account --global-var realm=registry --global-var public_url=http://localhost:5000/api/v1/ --global-var public_user_id=cfms-postman-public-user --global-var public_user_password=password +`./node_modules/newman/bin/newman.js run API_Test_TheQ_Booking.json -e postman_env.json --global-var userid=cfms-postman-operator --global-var password=password --global-var userid_nonqtxn=cfms-postman-non-operator --global-var password_nonqtxn=password --global-var client_secret=theq-local-dev-secret --global-var url=http://localhost:5000/api/v1/ --global-var auth_url=http://localhost:8085 --global-var clientid=theq-queue-management-api --global-var realm=servicebc-local --global-var public_url=http://localhost:5000/api/v1/ --global-var public_user_id=cfms-postman-public-user --global-var public_user_password=password ` +See `api/postman/README-local-auth.md` for local auth troubleshooting notes. + ## Jest Test ### Setup For Jest tests diff --git a/keycloak-local/servicebc-local-realm.json b/keycloak-local/servicebc-local-realm.json index d7a972105..82545d4d1 100644 --- a/keycloak-local/servicebc-local-realm.json +++ b/keycloak-local/servicebc-local-realm.json @@ -331,6 +331,17 @@ "access.token.claim": "true" } }, + { + "name": "audience-theq-notifications-api", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "theq-notifications-api", + "id.token.claim": "false", + "access.token.claim": "true" + } + }, { "name": "username", "protocol": "openid-connect", @@ -615,6 +626,93 @@ "realmRoles": [ "online_appointment_user" ] + }, + { + "username": "cfms-postman-operator", + "enabled": true, + "emailVerified": true, + "firstName": "Postman", + "lastName": "Operator", + "email": "cfms-postman-operator@example.com", + "attributes": { + "theq_username": [ + "cfms-postman-operator" + ], + "identity_provider": [ + "idir" + ], + "display_name": [ + "Postman Operator" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "internal_user" + ] + }, + { + "username": "cfms-postman-non-operator", + "enabled": true, + "emailVerified": true, + "firstName": "Postman", + "lastName": "Non Operator", + "email": "cfms-postman-non-operator@example.com", + "attributes": { + "theq_username": [ + "cfms-postman-non-operator" + ], + "identity_provider": [ + "idir" + ], + "display_name": [ + "Postman Non Operator" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "internal_user" + ] + }, + { + "username": "cfms-postman-public-user", + "enabled": true, + "emailVerified": true, + "firstName": "Postman", + "lastName": "Public User", + "email": "cfms-postman-public-user@example.com", + "attributes": { + "theq_username": [ + "cfms-postman-public-user" + ], + "identity_provider": [ + "bceid" + ], + "display_name": [ + "cfms-postman-public-user" + ] + }, + "credentials": [ + { + "type": "password", + "value": "password", + "temporary": false + } + ], + "realmRoles": [ + "online_appointment_user" + ] } ] } From cd52303ac086890cc105339a939c6dd024cd9c68 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 24 Apr 2026 15:24:31 -0700 Subject: [PATCH 41/81] Add notification checks for appointments --- .../appointment/appointment_delete.py | 4 +- .../bookings/appointment/appointment_post.py | 4 +- .../bookings/appointment/appointment_put.py | 17 +++-- .../appointment/appointment_reminder_get.py | 4 +- api/app/tests/flows/test_appointment_flows.py | 76 +++++++++++++++++++ api/app/tests/flows/test_reminder_flows.py | 73 ++++++++++++++++++ api/app/utilities/email.py | 12 +++ 7 files changed, 176 insertions(+), 14 deletions(-) diff --git a/api/app/resources/bookings/appointment/appointment_delete.py b/api/app/resources/bookings/appointment/appointment_delete.py index abe9b50e6..823cfd8c9 100644 --- a/api/app/resources/bookings/appointment/appointment_delete.py +++ b/api/app/resources/bookings/appointment/appointment_delete.py @@ -22,7 +22,7 @@ from app.schemas.bookings import AppointmentSchema from app.utilities.auth_util import Role, get_username from app.utilities.auth_util import is_public_user -from app.utilities.email import get_cancel_email_contents, send_email +from app.utilities.email import get_cancel_email_contents, send_email, can_send_service_notification from app.utilities.snowplow import SnowPlow from qsystem import application from qsystem import api, db, socketio @@ -55,7 +55,7 @@ def delete(self, id): # Do not log snowplow events or send emails if it's a draft. # If the appointment is public user's and if staff deletes it send email - if not appointment.is_draft and csr: + if can_send_service_notification(appointment) and csr: office = Office.find_by_id(appointment.office_id) diff --git a/api/app/resources/bookings/appointment/appointment_post.py b/api/app/resources/bookings/appointment/appointment_post.py index e8bd3f38e..1cc36e51a 100644 --- a/api/app/resources/bookings/appointment/appointment_post.py +++ b/api/app/resources/bookings/appointment/appointment_post.py @@ -27,7 +27,7 @@ from app.utilities.auth_util import Role, get_username from app.utilities.auth_util import is_public_user from app.utilities.email import get_confirmation_email_contents, send_email, \ - get_blackout_email_contents + get_blackout_email_contents, can_send_service_notification from app.utilities.snowplow import SnowPlow from qsystem import api, api_call_with_retry, db, my_print, application from qsystem import socketio @@ -176,7 +176,7 @@ def post(self): is_stat = (json_data.get('stat_flag', False)) - if ((not is_stat) and (not is_blackout_appt)): + if can_send_service_notification(appointment): # Send confirmation email and sms try: send_email(request.headers['Authorization'].replace('Bearer ', ''), *get_confirmation_email_contents(appointment, office, office.timezone, user)) diff --git a/api/app/resources/bookings/appointment/appointment_put.py b/api/app/resources/bookings/appointment/appointment_put.py index 38a18b9c6..d329da45d 100644 --- a/api/app/resources/bookings/appointment/appointment_put.py +++ b/api/app/resources/bookings/appointment/appointment_put.py @@ -23,7 +23,7 @@ from app.utilities.snowplow import SnowPlow from app.utilities.auth_util import is_public_user from app.utilities.auth_util import Role, get_username -from app.utilities.email import send_email, get_confirmation_email_contents +from app.utilities.email import send_email, get_confirmation_email_contents, can_send_service_notification from app.services import AvailabilityService from dateutil.parser import parse from qsystem import socketio, application @@ -130,13 +130,14 @@ def put(self, id): db.session.add(appointment) db.session.commit() - # Send confirmation email - try: - send_email(request.headers['Authorization'].replace('Bearer ', ''), *get_confirmation_email_contents(appointment, office, office.timezone, user)) - send_sms(appointment, office, office.timezone, user, - request.headers['Authorization'].replace('Bearer ', '')) - except Exception as exc: - logging.exception('Error on token generation - %s', exc) + if can_send_service_notification(appointment): + # Send confirmation email + try: + send_email(request.headers['Authorization'].replace('Bearer ', ''), *get_confirmation_email_contents(appointment, office, office.timezone, user)) + send_sms(appointment, office, office.timezone, user, + request.headers['Authorization'].replace('Bearer ', '')) + except Exception as exc: + logging.exception('Error on token generation - %s', exc) # Make Snowplow call. schema = 'appointment_update' diff --git a/api/app/resources/bookings/appointment/appointment_reminder_get.py b/api/app/resources/bookings/appointment/appointment_reminder_get.py index 2fb405114..a465bafa6 100644 --- a/api/app/resources/bookings/appointment/appointment_reminder_get.py +++ b/api/app/resources/bookings/appointment/appointment_reminder_get.py @@ -17,7 +17,7 @@ from app.models.bookings import Appointment from app.utilities.auth_util import Role, has_any_role from app.utilities.email import is_valid_email, formatted_date, get_email, \ - get_duration + get_duration, can_send_service_notification from qsystem import api, api_call_with_retry from app.auth.auth import jwt from app.utilities.sms import is_valid_phone, format_sms_date @@ -52,7 +52,7 @@ def get(self, reminder_type: str = 'email'): send_reminder = True user_telephone = appointment.contact_information - if send_reminder: + if send_reminder and can_send_service_notification(appointment): if reminder_type == 'email': date, day = formatted_date(appointment.start_time, timezone) else: diff --git a/api/app/tests/flows/test_appointment_flows.py b/api/app/tests/flows/test_appointment_flows.py index ad761d31e..eb386b3d8 100644 --- a/api/app/tests/flows/test_appointment_flows.py +++ b/api/app/tests/flows/test_appointment_flows.py @@ -6,10 +6,12 @@ from app.tests.api_test_support import ( assert_json_response, create_public_user, + future_utc_window, first_day_with_slots, json_of, public_slot_payload, slot_window_to_iso, + unique_name, ) from app.tests.api_test_support import ( create_internal_appointment as _create_internal_appointment, @@ -18,6 +20,24 @@ pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] +def _create_blackout_appointment(api_client, seeded_data, *, days_from_now: int) -> dict: + start_time, end_time = future_utc_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Blackout coverage", + "citizen_name": unique_name("blackout-appt"), + "contact_information": "blackout@example.com", + "blackout_flag": "Y", + }, + ) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + def test_internal_appointment_can_be_listed_and_retrieved( internal_ga_client, seeded_data ): @@ -73,6 +93,62 @@ def test_internal_appointment_can_be_deleted(internal_ga_client, seeded_data): assert response.status_code == 204, response.get_data(as_text=True) +def test_blackout_appointment_update_skips_notifications( + monkeypatch, internal_ga_client, seeded_data +): + """Assert that service-less blackout updates do not invoke notification builders.""" + appointment = _create_blackout_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + def _unexpected(*args, **kwargs): + raise AssertionError("blackout appointments should not trigger notifications") + + monkeypatch.setattr( + "app.resources.bookings.appointment.appointment_put.get_confirmation_email_contents", + _unexpected, + ) + monkeypatch.setattr( + "app.resources.bookings.appointment.appointment_put.send_sms", + _unexpected, + ) + + response = internal_ga_client.put( + f"/appointments/{appointment['appointment_id']}/", + json={ + "comments": "Blackout appointment updated", + "citizen_name": "Updated blackout", + "contact_information": "updated-blackout@example.com", + }, + ) + + assert_json_response(response, 200) + assert json_of(response)["appointment"]["comments"] == "Blackout appointment updated" + + +def test_blackout_appointment_delete_skips_notifications( + monkeypatch, internal_ga_client, seeded_data +): + """Assert that service-less blackout deletes do not invoke cancellation email builders.""" + appointment = _create_blackout_appointment( + internal_ga_client, seeded_data, days_from_now=2 + ) + + def _unexpected(*args, **kwargs): + raise AssertionError("blackout appointments should not trigger notifications") + + monkeypatch.setattr( + "app.resources.bookings.appointment.appointment_delete.get_cancel_email_contents", + _unexpected, + ) + + response = internal_ga_client.delete( + f"/appointments/{appointment['appointment_id']}/" + ) + + assert response.status_code == 204, response.get_data(as_text=True) + + def test_recurring_appointment_update_applies_to_each_occurrence( internal_ga_client, seeded_data ): diff --git a/api/app/tests/flows/test_reminder_flows.py b/api/app/tests/flows/test_reminder_flows.py index 381b3a3f1..3bd493728 100644 --- a/api/app/tests/flows/test_reminder_flows.py +++ b/api/app/tests/flows/test_reminder_flows.py @@ -6,10 +6,35 @@ configure_public_user_reminders, create_internal_reminder_appointment, ) +from app.tests.api_test_support import future_utc_window, unique_name pytestmark = [pytest.mark.flows, pytest.mark.usefixtures("seeded_database")] +def _create_blackout_reminder_appointment( + api_client, + seeded_data, + *, + contact_information: str, + days_from_now: int = 2, +) -> dict: + start_time, end_time = future_utc_window(days_from_now) + response = api_client.post( + "/appointments/", + json={ + "office_id": seeded_data["office_ids"]["test_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Blackout reminder coverage", + "citizen_name": unique_name("blackout-reminder"), + "contact_information": contact_information, + "blackout_flag": "Y", + }, + ) + assert_json_response(response, 201) + return json_of(response)["appointment"] + + def test_email_reminders_include_opted_in_public_users( monkeypatch, public_client, reminder_job_client, seeded_data ): @@ -81,3 +106,51 @@ def test_email_reminders_exclude_public_users_who_did_not_opt_in( assert_json_response(response, 200) assert json_of(response)["appointments"] == [] + + +def test_email_reminders_exclude_service_less_blackout_appointments( + internal_ga_client, monkeypatch, reminder_job_client, seeded_data +): + """Assert that blackout appointments without services are skipped by email reminders.""" + appointment = _create_blackout_reminder_appointment( + internal_ga_client, + seeded_data, + contact_information="blackout@example.com", + ) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["test_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/email/") + + assert_json_response(response, 200) + assert not any( + item["display_name"] == appointment["citizen_name"] + for item in json_of(response)["appointments"] + ) + + +def test_sms_reminders_exclude_service_less_blackout_appointments( + internal_ga_client, monkeypatch, reminder_job_client, seeded_data +): + """Assert that blackout appointments without services are skipped by SMS reminders.""" + appointment = _create_blackout_reminder_appointment( + internal_ga_client, + seeded_data, + contact_information="2505550199", + ) + align_current_pacific_time_for_appointment( + monkeypatch, + appointment["start_time"], + seeded_data["office_timezones"]["test_office"], + ) + + response = reminder_job_client.get("/appointment/reminders/sms/") + + assert_json_response(response, 200) + assert not any( + item["display_name"] == appointment["citizen_name"] + for item in json_of(response)["appointments"] + ) diff --git a/api/app/utilities/email.py b/api/app/utilities/email.py index fceca6606..15e8f96b2 100644 --- a/api/app/utilities/email.py +++ b/api/app/utilities/email.py @@ -87,6 +87,18 @@ def get_blackout_email_contents(blackout_appt: Appointment, cancelled_appointmen return subject, get_email(user, cancelled_appointment), sender, body +def can_send_service_notification(appointment: Appointment) -> bool: + """Return whether the appointment supports service-based notifications.""" + return bool( + appointment + and not appointment.is_draft + and appointment.blackout_flag != "Y" + and not appointment.stat_flag + and appointment.service_id is not None + and appointment.service is not None + ) + + def get_confirmation_email_contents(appointment: Appointment, office, timezone, user): """Send confirmation email""" sender = current_app.config.get('MAIL_FROM_ID') From b7a20db244dc2d63e15c43f35955de44594b9549 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Fri, 24 Apr 2026 16:14:43 -0700 Subject: [PATCH 42/81] Fix load test user to office mismatch --- .../bookings/appointment/appointment_post.py | 2 +- .../validation/test_appointment_validation.py | 25 +++++ tests/loadtesting/README.md | 16 ++-- tests/loadtesting/envs.example.sh | 2 +- tests/loadtesting/functions.js | 92 ++++++++++++++++++- 5 files changed, 127 insertions(+), 10 deletions(-) diff --git a/api/app/resources/bookings/appointment/appointment_post.py b/api/app/resources/bookings/appointment/appointment_post.py index 1cc36e51a..180523c2d 100644 --- a/api/app/resources/bookings/appointment/appointment_post.py +++ b/api/app/resources/bookings/appointment/appointment_post.py @@ -196,4 +196,4 @@ def post(self): "errors": {}}, 201 else: - return {"The Appointment Office ID and CSR Office ID do not match!"}, 403 + return {"message": "The Appointment Office ID and CSR Office ID do not match!"}, 403 diff --git a/api/app/tests/validation/test_appointment_validation.py b/api/app/tests/validation/test_appointment_validation.py index 39418998e..c014c1106 100644 --- a/api/app/tests/validation/test_appointment_validation.py +++ b/api/app/tests/validation/test_appointment_validation.py @@ -174,6 +174,31 @@ def test_internal_appointment_create_rejects_unknown_service_id( assert json_of(response)["message"] == "Could not find service for service_id: 999999" +def test_internal_appointment_create_rejects_mismatched_office_as_json( + internal_ga_client, seeded_data +): + """Assert that internal appointment creation returns a JSON 403 when the payload office differs from the CSR office.""" + start_time, end_time = future_utc_window(2) + response = internal_ga_client.post( + "/appointments/", + json={ + "service_id": seeded_data["service_ids"]["msp"], + "office_id": seeded_data["office_ids"]["limited_office"], + "start_time": start_time, + "end_time": end_time, + "comments": "Internal mismatched office", + "citizen_name": unique_name("mismatched-office"), + "contact_information": "internal@example.com", + }, + ) + + assert_json_response(response, 403) + assert ( + json_of(response)["message"] + == "The Appointment Office ID and CSR Office ID do not match!" + ) + + def test_public_appointment_create_rejects_unknown_service_id( public_client, seeded_data ): diff --git a/tests/loadtesting/README.md b/tests/loadtesting/README.md index 3386dbe9f..baebd1430 100644 --- a/tests/loadtesting/README.md +++ b/tests/loadtesting/README.md @@ -13,7 +13,7 @@ - [Resources](#resources) - [FAQ / Troubleshooting](#faq--troubleshooting) - [Verify IDs are correct](#verify-ids-are-correct) - - [Verify the "admin" user is assigned to correct office](#verify-the-admin-user-is-assigned-to-correct-office) + - [Verify the Load-Test User Is Assigned to the Correct Office](#verify-the-load-test-user-is-assigned-to-the-correct-office) - [I get errors when testing locally, but not when testing OpenShift dev](#i-get-errors-when-testing-locally-but-not-when-testing-openshift-dev) ## Installation @@ -57,7 +57,7 @@ Important local defaults: * Keycloak realm: `servicebc-local` * Keycloak client id: `theq-queue-management-api` * Keycloak client secret: `theq-local-dev-secret` -* Demo load-test user: `admin@idir` +* Demo load-test user: `cfms-postman-operator` * Demo load-test password: `password` `bootstrap` wipes and recreates development data. The default load test IDs in `envs.sh` assume that freshly bootstrapped dataset. @@ -89,7 +89,7 @@ Configuration of varables is done in `envs.sh`. The main variables that will be * `MAX_VIRTUAL_USERS` - determines the maximum amount of concurrent virtual users that are accessing the system at once * `TARGET` - the endpoint being load tested. The default is the local API at `http://localhost:5000` * `KEYCLOAK_BASE_URL`, `KEYCLOAK_REALM`, `KEYCLOAK_CLIENT_ID`, `KEYCLOAK_CLIENT_SECRET` - local Keycloak token settings -* `KEYCLOAK_USERNAME`, `KEYCLOAK_PASSWORD` - the demo user used to mint the token for the load test +* `KEYCLOAK_USERNAME`, `KEYCLOAK_PASSWORD` - the demo user used to mint the token for the load test. The checked-in default is `cfms-postman-operator`. * `LOADTEST_OFFICE_ID`, `LOADTEST_CREATE_SERVICE_ID`, `LOADTEST_UPDATE_SERVICE_ID` - seeded local data IDs used by the appointment scenarios * `LOADTEST_DRAFT_OFFICE_ID`, `LOADTEST_DRAFT_SERVICE_ID` - seeded local data IDs used for draft creation. The local bootstrap default uses office `2` because office `1` has no appointment timeslots. * `LOADTEST_OFFICE_TIMEZONE` - IANA timezone used to turn slot times into draft appointment timestamps. The local bootstrap default is `America/Vancouver`. @@ -316,11 +316,15 @@ The default values assume `uv run python manage.py bootstrap` has been run local * `LOADTEST_CREATE_SERVICE_ID=11` * `LOADTEST_UPDATE_SERVICE_ID=7` -### Verify the "admin" user is assigned to correct office +### Verify the Load-Test User Is Assigned to the Correct Office -The admin user must be assigned to the same office that the tests try to use. +The seeded load-test user must be assigned to the same office that the tests try to use. -For example, if `LOADTEST_OFFICE_ID=1`, then the app-level `admin` CSR record must still be assigned to Test Office. With a fresh local bootstrap, that is the default arrangement. +With the checked-in defaults, `KEYCLOAK_USERNAME=cfms-postman-operator` should resolve to the app-level CSR `cfms-postman-operator`, which should belong to Test Office (`LOADTEST_OFFICE_ID=1`) after a fresh local `bootstrap`. + +If you copied `envs.example.sh` to `envs.sh` before this change, your local `envs.sh` may still override the checked-in defaults with an older username such as `admin@idir`. + +The load-test harness checks `/api/v1/csrs/me/` before proceeding with authenticated requests. If the CSR office and `LOADTEST_OFFICE_ID` differ, the run fails fast and tells you which `KEYCLOAK_USERNAME` was used, which office it resolved to, and that you should switch to `cfms-postman-operator` or re-bootstrap/reconfigure the chosen user. ### I get errors when testing locally, but not when testing OpenShift dev diff --git a/tests/loadtesting/envs.example.sh b/tests/loadtesting/envs.example.sh index fa6d2b95a..190ad4e70 100644 --- a/tests/loadtesting/envs.example.sh +++ b/tests/loadtesting/envs.example.sh @@ -10,7 +10,7 @@ export KEYCLOAK_BASE_URL="http://localhost:8085/auth" export KEYCLOAK_REALM="servicebc-local" export KEYCLOAK_CLIENT_ID="theq-queue-management-api" export KEYCLOAK_CLIENT_SECRET="theq-local-dev-secret" -export KEYCLOAK_USERNAME="admin@idir" +export KEYCLOAK_USERNAME="cfms-postman-operator" export KEYCLOAK_PASSWORD="password" # These IDs assume a local database seeded with `uv run python manage.py bootstrap`. diff --git a/tests/loadtesting/functions.js b/tests/loadtesting/functions.js index 863621280..292b7c9c4 100644 --- a/tests/loadtesting/functions.js +++ b/tests/loadtesting/functions.js @@ -5,7 +5,7 @@ const DEFAULT_KEYCLOAK_BASE_URL = 'http://localhost:8085/auth'; const DEFAULT_KEYCLOAK_REALM = 'servicebc-local'; const DEFAULT_KEYCLOAK_CLIENT_ID = 'theq-queue-management-api'; const DEFAULT_KEYCLOAK_CLIENT_SECRET = 'theq-local-dev-secret'; -const DEFAULT_KEYCLOAK_USERNAME = 'admin@idir'; +const DEFAULT_KEYCLOAK_USERNAME = 'cfms-postman-operator'; const DEFAULT_KEYCLOAK_PASSWORD = 'password'; const DEFAULT_TARGET = 'http://localhost:5000'; const DEFAULT_OFFICE_TIMEZONE = 'America/Vancouver'; @@ -18,6 +18,7 @@ const DEFAULT_DRAFT_SLOT_WEEK_RANGE = 300000; // As most load testing is short lived (minutes, not hours) this works fine. // Cache auth tokens to a plain old JavaScript object const authTokenList = {}; +const loadTestOfficeAlignmentChecks = {}; function getKeycloakConfig() { return { @@ -197,9 +198,96 @@ async function loginToKeycloak(username, password) { return tokenResponse; } +function extractCsrOffice(payload) { + const csr = payload && payload.csr; + const officeId = csr && csr.office_id; + const officeName = csr && csr.office && csr.office.office_name; + + if (typeof officeId !== 'number') { + throw new Error(`Unable to determine CSR office from /api/v1/csrs/me/: ${JSON.stringify(payload)}`); + } + + return { + officeId, + officeName: officeName || `office ${officeId}`, + }; +} + +async function fetchCsrSelf(accessToken, target) { + const url = buildApiUrl(target, '/api/v1/csrs/me/'); + const res = await fetch(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + cookie: `oidc-jwt=${accessToken}`, + }, + method: 'GET', + }); + const responseBody = await res.text(); + let payload; + + try { + payload = JSON.parse(responseBody); + } catch (error) { + throw new Error(`Unable to parse /api/v1/csrs/me/ response for load-test preflight: ${responseBody}`); + } + + return { res, payload }; +} + +async function assertLoadTestOfficeAlignment(accessToken, cacheKey, username, password) { + const { target, officeId } = getLoadTestConfig(); + let currentAccessToken = accessToken; + let { res, payload } = await fetchCsrSelf(currentAccessToken, target); + + // Retry once with a freshly minted token to smooth over transient startup + // or token-validation hiccups without hiding persistent auth problems. + if (res.status === 401) { + const refreshedToken = await loginToKeycloak(username, password); + authTokenList[cacheKey] = refreshedToken; + currentAccessToken = refreshedToken.access_token; + ({ res, payload } = await fetchCsrSelf(currentAccessToken, target)); + } + + if (!res.ok) { + throw new Error(`Load-test preflight failed when checking /api/v1/csrs/me/: ${res.status} ${JSON.stringify(payload)}`); + } + + const actualOffice = extractCsrOffice(payload); + if (actualOffice.officeId !== officeId) { + throw new Error( + `Load-test auth mismatch: KEYCLOAK_USERNAME=${username} resolved to CSR office ${actualOffice.officeId} (${actualOffice.officeName}), but LOADTEST_OFFICE_ID=${officeId}. Switch KEYCLOAK_USERNAME to cfms-postman-operator in tests/loadtesting/envs.sh or your shell, or re-bootstrap/reconfigure that user so the offices match.` + ); + } + + loadTestOfficeAlignmentChecks[cacheKey] = Promise.resolve({ + ...actualOffice, + accessToken: currentAccessToken, + }); + return { + ...actualOffice, + accessToken: currentAccessToken, + }; +} + async function applyAuthHeader(requestParams) { const { username, password } = getKeycloakCredentials(); - const { access_token } = await getAuthToken(username, password); + let { access_token } = await getAuthToken(username, password); + const authCacheKey = getAuthCacheKey(username); + + if (!loadTestOfficeAlignmentChecks[authCacheKey]) { + loadTestOfficeAlignmentChecks[authCacheKey] = assertLoadTestOfficeAlignment( + access_token, + authCacheKey, + username, + password + ).catch((error) => { + delete loadTestOfficeAlignmentChecks[authCacheKey]; + throw error; + }); + } + + const alignment = await loadTestOfficeAlignmentChecks[authCacheKey]; + access_token = alignment.accessToken; requestParams.headers = requestParams.headers || {}; requestParams.headers.Authorization = `Bearer ${access_token}`; From 8a98f0cc6b10504fcd2cea42f7068851ebb44cb0 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Mon, 27 Apr 2026 11:50:18 -0700 Subject: [PATCH 43/81] Fix blank username with invalid token --- api/app/models/theq/csr.py | 4 ++ api/app/models/theq/public_user.py | 4 ++ api/app/resources/theq/login.py | 19 +++-- api/app/resources/theq/user/user.py | 10 +-- api/app/tests/auth/test_public_user_routes.py | 72 +++++++++++++++++++ api/app/tests/fixtures/auth.py | 11 ++- api/app/tests/fixtures/db.py | 15 ++++ api/app/utilities/auth_util.py | 24 ++++--- 8 files changed, 133 insertions(+), 26 deletions(-) diff --git a/api/app/models/theq/csr.py b/api/app/models/theq/csr.py index cf2e4bb25..21651deee 100644 --- a/api/app/models/theq/csr.py +++ b/api/app/models/theq/csr.py @@ -51,6 +51,8 @@ def __init__(self, **kwargs): @classmethod def find_by_username(cls, username): + if not username or not username.strip(): + return None # Possible keycloak->TheQ id values are user@idir->user, idir/user->user or user@bceid->user@bceid idir_id = username.split("idir/")[-1].lower() if "@idir" in username: @@ -77,6 +79,8 @@ def find_by_userid(cls, userid): @classmethod def delete_user_cache(cls, username): + if not username or not username.strip(): + return idir_id = username.split("idir/")[-1] key = (CSR.format_string % idir_id).lower() cache.delete(key) diff --git a/api/app/models/theq/public_user.py b/api/app/models/theq/public_user.py index c172cc673..fa50d346e 100644 --- a/api/app/models/theq/public_user.py +++ b/api/app/models/theq/public_user.py @@ -38,6 +38,8 @@ def __init__(self, **kwargs): @classmethod def find_by_username(cls, username): """Find User records by username.""" + if not username or not username.strip(): + return None user = cls.query.filter_by(username=username).one_or_none() return user @@ -50,6 +52,8 @@ def find_by_user_id(cls, user_id): @classmethod def find_appointments_by_username(cls, username: str): """Find all appointments for the user.""" + if not username or not username.strip(): + return [] today = datetime.now(timezone.utc) query = db.session.query(Appointment) \ diff --git a/api/app/resources/theq/login.py b/api/app/resources/theq/login.py index 86e0973a5..d5b55f855 100644 --- a/api/app/resources/theq/login.py +++ b/api/app/resources/theq/login.py @@ -30,18 +30,15 @@ class Login(Resource): @jwt.requires_auth_cookie def get(self): username = get_username() - if username != '': - csr = CSR.find_by_username(username) - if csr: - login_user(csr) - if application.config['USE_HTTPS']: - return redirect(url_for(admin_index_const, - _scheme=application.config['PREFERRED_URL_SCHEME'], - _external=application.config['USE_HTTPS'])) - else: - return redirect(url_for(admin_index_const)) + csr = CSR.find_by_username(username) + if csr: + login_user(csr) + if application.config['USE_HTTPS']: + return redirect(url_for(admin_index_const, + _scheme=application.config['PREFERRED_URL_SCHEME'], + _external=application.config['USE_HTTPS'])) else: - return abort(401, self.auth_string) + return redirect(url_for(admin_index_const)) else: return abort(401, self.auth_string) diff --git a/api/app/resources/theq/user/user.py b/api/app/resources/theq/user/user.py index b89c10531..0071aadaa 100644 --- a/api/app/resources/theq/user/user.py +++ b/api/app/resources/theq/user/user.py @@ -53,10 +53,11 @@ class PublicUsers(Resource): def post(self): try: user_info = g.jwt_oidc_token_info - user: PublicUserModel = PublicUserModel.find_by_username(get_username()) + username = get_username() + user: PublicUserModel = PublicUserModel.find_by_username(username) if not user: user = PublicUserModel() - user.username = get_username() + user.username = username user.email = user_info.get('email') else: # update email only if the email is None for existing user if not user.email: @@ -84,7 +85,8 @@ class PublicUser(Resource): def put(self, user_id: int): try: json_data = request.get_json() - user: PublicUserModel = PublicUserModel.find_by_username(get_username()) + username = get_username() + user: PublicUserModel = PublicUserModel.find_by_username(username) current_sms_reminder: bool = user.send_sms_reminders user.email = json_data.get('email') user.telephone = json_data.get('telephone') @@ -96,7 +98,7 @@ def put(self, user_id: int): # If the user is opting in for SMS reminders, send reminders for all the appointments. if not current_sms_reminder and user.send_sms_reminders: appointments: List[AppointmentModel] = PublicUserModel.find_appointments_by_username( - get_username()) + username) for appointment in appointments: office = appointment.office send_sms(appointment, office, office.timezone, user, diff --git a/api/app/tests/auth/test_public_user_routes.py b/api/app/tests/auth/test_public_user_routes.py index e0a4eb2bd..192403503 100644 --- a/api/app/tests/auth/test_public_user_routes.py +++ b/api/app/tests/auth/test_public_user_routes.py @@ -74,6 +74,27 @@ def _context(bare_client, internal_ga_client, public_client, seeded_data): } +def _seed_blank_public_user(app): + with app.app_context(): + from app.models.theq import PublicUser + from qsystem import db + + user = PublicUser.query.filter_by(username="").one_or_none() + if user is None: + user = PublicUser( + username="", + display_name="Legacy Blank User", + last_name="Legacy", + email="legacy-blank@example.com", + telephone="2505550199", + send_email_reminders=False, + send_sms_reminders=False, + ) + db.session.add(user) + db.session.commit() + return user.user_id + + @pytest.mark.parametrize("case", PUBLIC_ROUTE_CASES, ids=lambda case: case.id) def test_bare_client_receives_401_for_public_user_routes( bare_client, case, internal_ga_client, public_client, seeded_data @@ -128,3 +149,54 @@ def test_public_user_create_backfills_legacy_required_fields(public_client_alt): assert user["telephone"] == "" assert user["send_email_reminders"] is False assert user["send_sms_reminders"] is False + + +def test_malformed_public_identity_cannot_create_or_match_blank_user( + public_client_malformed, app +): + """Assert that malformed public identities fail auth instead of matching a legacy blank username row.""" + blank_user_id = _seed_blank_public_user(app) + + response = public_client_malformed.post("/users/") + + assert_unauthorized(response) + + with app.app_context(): + from app.models.theq import PublicUser + + blank_user = PublicUser.find_by_user_id(blank_user_id) + assert blank_user is not None + assert blank_user.display_name == "Legacy Blank User" + assert PublicUser.query.filter_by(username="").count() == 1 + + +def test_malformed_public_identity_cannot_read_me_as_blank_user( + public_client_malformed, app +): + """Assert that malformed public identities cannot resolve /users/me/ to a blank username row.""" + _seed_blank_public_user(app) + + response = public_client_malformed.get("/users/me/") + + assert_unauthorized(response) + + +def test_public_identity_without_username_claim_cannot_read_me_as_blank_user( + public_client_missing_username, app +): + """Assert that a public token missing the username claim cannot resolve /users/me/ to the legacy blank user.""" + _seed_blank_public_user(app) + + response = public_client_missing_username.get("/users/me/") + + assert_unauthorized(response) + + +def test_blank_username_lookups_are_rejected(app): + """Assert that blank usernames no longer resolve public-user or CSR lookups.""" + with app.app_context(): + from app.models.theq import CSR, PublicUser + + assert PublicUser.find_by_username("") is None + assert PublicUser.find_appointments_by_username("") == [] + assert CSR.find_by_username("") is None diff --git a/api/app/tests/fixtures/auth.py b/api/app/tests/fixtures/auth.py index dbc5da078..89ed446dd 100644 --- a/api/app/tests/fixtures/auth.py +++ b/api/app/tests/fixtures/auth.py @@ -30,6 +30,16 @@ def public_client_alt(api_client_factory): return api_client_factory("public_user_alt") +@pytest.fixture() +def public_client_malformed(api_client_factory): + return api_client_factory("public_user_malformed") + + +@pytest.fixture() +def public_client_missing_username(api_client_factory): + return api_client_factory("public_user_missing_username") + + @pytest.fixture() def reminder_job_client(api_client_factory): return api_client_factory("reminder_job") @@ -38,4 +48,3 @@ def reminder_job_client(api_client_factory): @pytest.fixture() def bare_client(api_client_factory): return api_client_factory(identity_name=None, token=None) - diff --git a/api/app/tests/fixtures/db.py b/api/app/tests/fixtures/db.py index 684a48419..c68788bf4 100644 --- a/api/app/tests/fixtures/db.py +++ b/api/app/tests/fixtures/db.py @@ -49,6 +49,21 @@ "display_name": "Public Alt User", "family_name": "PublicAlt", }, + "public_user_malformed": { + "username": " ", + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public-malformed@example.com", + "display_name": "Public Malformed User", + "family_name": "PublicMalformed", + }, + "public_user_missing_username": { + "identity_provider": "bceid", + "realm_access": {"roles": ["online_appointment_user"]}, + "email": "public-missing-username@example.com", + "display_name": "Public Missing Username User", + "family_name": "PublicMissingUsername", + }, "reminder_job": { "username": "theq-reminder-job", "identity_provider": "idir", diff --git a/api/app/utilities/auth_util.py b/api/app/utilities/auth_util.py index cb6399d88..d707073f2 100644 --- a/api/app/utilities/auth_util.py +++ b/api/app/utilities/auth_util.py @@ -28,17 +28,21 @@ class Role(Enum): def get_username() -> str: """ Gets the username in the form user@idp, where username is lowercase - and the idp is typically one of "bceid", "bcsc", or "idir". Will - return an empty string if there is no authenticated user identity. + and the idp is typically one of "bceid", "bcsc", or "idir". Abort + with 401 when the authenticated identity is missing or malformed. """ - if 'username' not in g.jwt_oidc_token_info or \ - 'identity_provider' not in g.jwt_oidc_token_info: - return '' - - username = g.jwt_oidc_token_info['username'].lower() + '@' + \ - g.jwt_oidc_token_info['identity_provider'] - - return username + token_info = getattr(g, 'jwt_oidc_token_info', None) + if token_info is None: + abort(401) + username = token_info.get('username') + identity_provider = token_info.get('identity_provider') + if not isinstance(username, str) or not isinstance(identity_provider, str): + abort(401) + username = username.strip().lower() + identity_provider = identity_provider.strip().lower() + if username == '' or identity_provider == '': + abort(401) + return username + '@' + identity_provider def is_public_user() -> bool: """Return if the user is a public user or not.""" From 68b4ede80fc75447797ee4a6477c01d30f2b48ec Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Mon, 27 Apr 2026 14:04:31 -0700 Subject: [PATCH 44/81] Update CODEOWNERS --- .github/CODEOWNERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2dd05100e..0d7a10abe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,2 @@ -# As a security feature, add these people as reviewers if a PR is created for -# anything under .github, including GitHub Actions and this file itself. # Define code owners for the entire repository -* @chrsamp @josekudiyirippil @Rajandeep98 \ No newline at end of file +* @chrsamp @josekudiyirippil @veenupunyani From 64e2b9ad6467b9d5baeb004788ff822047aaa3ec Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Mon, 27 Apr 2026 16:50:45 -0700 Subject: [PATCH 45/81] Update devcontainer config --- .devcontainer/Dockerfile | 4 +- .devcontainer/devcontainer.json | 8 +- .devcontainer/docker-compose.yml | 109 -- .devcontainer/postCreateCommand.sh | 149 +-- README.md | 204 ++- appointment-frontend/package-lock.json | 418 +----- compose.yaml | 79 ++ frontend/package-lock.json | 1713 ++++++++++++++++++------ scripts/setup-local-config.sh | 90 ++ 9 files changed, 1752 insertions(+), 1022 deletions(-) delete mode 100644 .devcontainer/docker-compose.yml create mode 100755 scripts/setup-local-config.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 331efd3a3..23fdcb866 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -22,7 +22,7 @@ # 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, # 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster # -# Note: this ARG is overwritten by the value in docker-compose.yml. +# Note: this ARG is overwritten by the value in compose.yaml. ARG VARIANT=3-bullseye FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} @@ -31,7 +31,7 @@ ENV PYTHONUNBUFFERED 1 # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 # -# Note: this ARG is overwritten by the value in docker-compose.yml. +# Note: this ARG is overwritten by the value in compose.yaml. ARG NODE_VERSION="none" RUN if [ "${NODE_VERSION}" != "none" ]; then \ su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fe16a573f..a7c7420fa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,9 +15,9 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, // see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/python-3-postgres -// Update the VARIANT arg in docker-compose.yml to pick a Python version +// Update the VARIANT arg in compose.yaml to pick a Python version { - "dockerComposeFile": "docker-compose.yml", + "dockerComposeFile": "../compose.yaml", // Add the extensions you want installed when the container is created. "customizations": { @@ -79,6 +79,10 @@ "label": "Appointments Frontend", "onAutoForward": "silent" }, + "8085": { + "label": "Keycloak", + "onAutoForward": "silent" + }, "10000": { "label": "Xpra (Cypress)", "onAutoForward": "silent" diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml deleted file mode 100644 index e029f6ce1..000000000 --- a/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2022 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -services: - # The container that contains all the code in the repository, plus the - # installations of Python requirements and Node packages. - app: - build: - args: - # Update 'VARIANT' to pick a version of Python: 3, 3.10, 3.9, 3.8, - # 3.7, 3.6. Append -bullseye or -buster to pin to an OS version. Use - # -bullseye variants on local arm64/Apple Silicon. - # - # Note: Tying ourselves to Python 3.8.8 to match the runtime version - # means that we are tied to old vscode images. We should consider an - # update to the runtime version. The list of versions available is at - # https://hub.docker.com/_/microsoft-vscode-devcontainers - # https://mcr.microsoft.com/v2/vscode/devcontainers/python/tags/list - VARIANT: '3.14-bookworm' - - # Node.js version to install - NODE_VERSION: '20' - - context: '..' - - dockerfile: '.devcontainer/Dockerfile' - - # Overrides default command so things don't shut down after the process - # ends. - command: 'sleep infinity' - - environment: - UV_PROJECT_ENVIRONMENT: '/workspace/api/.venv' - - # For container "db". - DATABASE_NAME: 'postgres' - DATABASE_PASSWORD: 'postgres' - - # For container "x11" used by Cypress. - DISPLAY: ':14' - LIBGL_ALWAYS_INDIRECT: '0' - - init: true - - # Runs app on the same network as the database container, allows - # "forwardPorts" in devcontainer.json function. - network_mode: 'service:db' - - volumes: - - x11:/tmp/.X11-unix:rw - - ..:/workspace:cached - - api-venv:/workspace/api/.venv - - appointment-frontend-node-modules:/workspace/appointment-frontend/node_modules - - frontend-node-modules:/workspace/frontend/node_modules - - # Run PostgreSQL as the database used by the API. Keep the version in sync - # with what is used in the runtime deployments. - db: - container_name: queue-management_devcontainer_db_1 - environment: - POSTGRES_DB: 'postgres' - POSTGRES_PASSWORD: 'postgres' - POSTGRES_USER: 'postgres' - - image: 'postgres:16.2' - - restart: 'unless-stopped' - - volumes: - - 'postgres-data:/var/lib/postgresql/data' - - # Display X clients (like Cypress) on an X server that uses Xpra to forward - # the display. Set the XPRA_PASSWORD password below and use a browser outside - # Docker to view the clients: - # http://localhost:10000/index.html?encoding=rgb32&password=XPRA_PASSWORD - x11: - environment: - DISPLAY: ':14' - MODE: 'tcp' - XPRA_HTML: 'yes' - XPRA_PASSWORD: 'password' - - image: 'jare/x11-bridge' - - ports: - - '10000:10000' - - restart: 'always' - - volumes: - - 'x11:/tmp/.X11-unix:rw' - -volumes: - postgres-data: - x11: - api-venv: - appointment-frontend-node-modules: - frontend-node-modules: diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index ae1e341e5..fbf68b37f 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -14,15 +14,12 @@ # License for the specific language governing permissions and limitations under # the License. +set -euo pipefail + ############################################################################### -# Functions +# Logging ############################################################################### -COLOR_DEFAULT='\033[0m' -COLOR_FAILURE='\033[0;31m' - -# Log the output to make it easier to find when things go wrong. - LOGDIR=".devcontainer/logs" LOGFILE="$LOGDIR/error.log" @@ -35,58 +32,12 @@ touch $LOGFILE # Redirect stderr to both the logfile and terminal using tee exec 2> >(tee -a $LOGFILE >&2) -# Echo a string in red. -# -# Parameter: string -# echo_failure () { - echo -e "$COLOR_FAILURE$*$COLOR_DEFAULT" | tee -a $LOGFILE >&2 -} - -# If a configuration file doesn't exist then create a default file. -# -# Parameters: source_file destination_file -# -copy_config () { - SOURCE=$1 - DESTINATION=$2 - - if [ ! -f $SOURCE ]; then - echo_failure configuration source file $(pwd)/$SOURCE is missing - else - if [ -f $DESTINATION ]; then - echo Using pre-existing file $(realpath $DESTINATION) - else - SOURCE=$(realpath $SOURCE) - - DIRECTORY=$(dirname $DESTINATION) - if [ ! -d $DIRECTORY ]; then - echo Creating directory $(pwd)/$DIRECTORY - mkdir -p $DIRECTORY - fi - - echo Copying $SOURCE to $(pwd)/$DESTINATION - cp $SOURCE $DESTINATION - fi - fi + echo "$*" | tee -a "$LOGFILE" >&2 } -# Check that a configuration value is defined in a file. As this only does a -# grep for the key, it is likely to provide false positives for comments, -# substrings of other keys, or keys defined without values. -# -# Parameters: filename key_name -# -check_setting () { - FILENAME=$1 - KEY_NAME=$2 - - grep "$KEY_NAME" $FILENAME > /dev/null - - if [ $? -ne 0 ]; then - echo_failure Missing configuration key $KEY_NAME in \ - $(realpath $FILENAME) - fi +run_setup_local_config () { + /bin/bash scripts/setup-local-config.sh } ############################################################################### @@ -101,9 +52,23 @@ install_api_deps () { if [ -d .venv ]; then sudo chown -R $(id -u):$(id -g) .venv fi - python3 -m pip install --upgrade pip -q || echo_failure "Failed to upgrade pip." - python3 -m pip install uv -q || echo_failure "Failed to install uv." - uv sync --group dev || echo_failure "Failed to sync API dependencies." + python3 -m pip install --upgrade pip -q + python3 -m pip install uv -q + uv sync --group dev + ) +} + +install_notifications_api_deps () { + ( + cd notifications-api + export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv" + # Ensure the environment directory is owned by the current user + if [ -d .venv ]; then + sudo chown -R $(id -u):$(id -g) .venv + fi + python3 -m pip install --upgrade pip -q + python3 -m pip install uv -q + uv sync --group dev ) } @@ -132,7 +97,36 @@ install_frontend_deps () { ) } +get_admin_csr_count () { + local table_exists + local count + + table_exists=$(PGPASSWORD=postgres psql -h localhost -U postgres -d postgres \ + -tA -c "SELECT to_regclass('public.csr') IS NOT NULL;") + + if [ "$table_exists" != "t" ]; then + echo_failure "The csr table is missing after migrations; aborting bootstrap." + return 1 + fi + + count=$(PGPASSWORD=postgres psql -h localhost -U postgres -d postgres \ + -tA -c "SELECT COUNT(*) FROM csr WHERE username = 'admin';") + + case "$count" in + ''|*[!0-9]*) + echo_failure "Unexpected csr count result: '$count'" + return 1 + ;; + esac + + echo "$count" +} + +echo +run_setup_local_config + install_api_deps +install_notifications_api_deps install_appointment_frontend_deps install_frontend_deps @@ -144,15 +138,22 @@ bootstrap_database () { ( cd api export UV_PROJECT_ENVIRONMENT="$(pwd)/.venv" - SEARCH_USER=admin + local count + + # Bootstrap runs inside the devcontainer, so it needs + # container-reachable Keycloak metadata even though the copied + # local .env remains host-friendly for non-container use. + export JWT_OIDC_WELL_KNOWN_CONFIG="http://keycloak:8080/auth/realms/servicebc-local/.well-known/openid-configuration" + export JWT_OIDC_JWKS_URI="http://keycloak:8080/auth/realms/servicebc-local/protocol/openid-connect/certs" + export JWT_OIDC_ISSUER="http://localhost:8085/auth/realms/servicebc-local" + uv run python manage.py db upgrade # If the default bootstrap admin CSR is missing, we're probably # starting with a clean database and need to bootstrap it. uv run python manage.py migrate_db - COUNT=$(PGPASSWORD=postgres psql -h queue-management_devcontainer_db_1 \ - -U postgres -tA -c "SELECT COUNT(*) FROM csr WHERE username = '$SEARCH_USER';") - if [ "$COUNT" -eq 0 ]; then + count=$(get_admin_csr_count) + if [ "$count" -eq 0 ]; then uv run python manage.py bootstrap fi ) @@ -161,28 +162,8 @@ bootstrap_database () { bootstrap_database ############################################################################### -# Configuration Files Setup and Checking +# Configuration Files Setup ############################################################################### echo - -copy_config .devcontainer/config/api/dotenv api/.env -check_setting api/.env JWT_OIDC_AUDIENCE -check_setting api/.env JWT_OIDC_WELL_KNOWN_CONFIG - -copy_config .devcontainer/config/api/client_secrets/secrets.json \ - api/client_secrets/secrets.json - -copy_config .devcontainer/config/frontend/public/static/keycloak/keycloak.json \ - frontend/public/static/keycloak/keycloak.json - -copy_config .devcontainer/config/frontend/public/config/configuration.json \ - frontend/public/config/configuration.json - -copy_config .devcontainer/config/appointment-frontend/dotenv.local appointment-frontend/.env.local - -copy_config .devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json \ - appointment-frontend/public/config/kc/keycloak-public.json - -copy_config .devcontainer/config/appointment-frontend/public/config/configuration.json \ - appointment-frontend/public/config/configuration.json +run_setup_local_config diff --git a/README.md b/README.md index 5644ff714..eb427c15a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,16 @@ Service BC connects people with services offered by the B.C. provincial governme ### `api` -The primary backend service for the platform. It provides the REST API and Socket.IO endpoints used by the staff and public frontends for queue operations, office administration, appointments, walk-ins, exams, uploads, smartboard data, and real-time updates. +The primary backend service for the platform. It serves the main REST API under `/api/v1` plus Socket.IO endpoints used by the staff and public frontends. + +Key responsibilities include: + +- Queue and citizen service flows: create and manage citizens, service requests, queue state transitions, invite and serve flows, hold flows, and completion flows. +- Office and reference data: offices, services, categories, channels, CSR state, user context, and related administrative data. +- Appointments, bookings, and walk-ins: appointment slots, appointment creation and updates, recurring bookings, walk-in queue support, and reminder-related workflows. +- Exams, rooms, and invigilators: exam scheduling, uploads, exports, room management, and invigilator management. +- Smartboard and real-time updates: smartboard data endpoints plus Socket.IO events for queue changes and office-specific live updates. +- Health endpoints: readiness and health checks for operational monitoring. ### `frontend` @@ -20,7 +29,7 @@ The public-facing Vue 2 application for booking appointments, viewing booked app ### `notifications-api` -A separate Flask service for outbound notifications. It exposes authenticated SMS and email endpoints and supports pluggable delivery providers, including GC Notify, CHES, and logging/custom implementations. +A separate Flask service for outbound notifications. It exposes authenticated `POST /api/v1/notifications/sms` and `POST /api/v1/notifications/email` endpoints and supports pluggable delivery providers, including GC Notify, CHES, and logging/custom implementations. ### `feedback-api` @@ -29,46 +38,6 @@ A separate Flask service for outbound notifications. It exposes authenticated SM This legacy Flask service accepts feedback submissions and forwards them to the older Camunda-based feedback flow. -## API Overview - -The primary API is served from `api` and exposes endpoints under `/api/v1`. - -- Queue and citizen service flows: create and manage citizens, service requests, queue state transitions, invite and serve flows, hold flows, and completion flows. -- Office and reference data: offices, services, categories, channels, CSR state, user context, and related administrative data. -- Appointments, bookings, and walk-ins: appointment slots, appointment creation and updates, recurring bookings, walk-in queue support, and reminder-related workflows. -- Exams, rooms, and invigilators: exam scheduling, uploads, exports, room management, and invigilator management. -- Smartboard and real-time updates: smartboard data endpoints plus Socket.IO events for queue changes and office-specific live updates. -- Health endpoints: readiness and health checks for operational monitoring. - -## Supporting APIs - -### `notifications-api` - -The notifications service exposes: - -- `POST /api/v1/notifications/sms` -- `POST /api/v1/notifications/email` - -These endpoints are authenticated and are used for outbound text and email delivery. - -### `feedback-api` - -The legacy feedback service exposes: - -- `POST /api/v1/feedback` - -It exists for backward compatibility only and should not be treated as a core long-term service. - -## Frontends - -### Staff frontend - -The [`frontend`](./frontend) application is the internal operational interface used by Service BC staff. It includes queue dashboards, admin screens, appointment and exam workflows, upload tools, smartboard support, and integrations with other line-of-business applications. - -### Public appointment frontend - -The [`appointment-frontend`](./appointment-frontend) application is the public web experience. It supports appointment booking, reviewing booked appointments, sign-in and account management, and walk-in queue status pages. - ## Technology Stack - Backend: Python, Flask, Flask-RESTX, SQLAlchemy, Flask-Migrate, Flask-SocketIO, Marshmallow, Gunicorn, and Gevent. @@ -82,8 +51,8 @@ Older references to RabbitMQ remain in the repository, but they are not part of This repository supports two local development workflows: -- Devcontainer -- Local host +- Using a [development container](https://containers.dev/) +- Developing locally on your host machine ### Dev Container Workflow @@ -95,10 +64,14 @@ This repository supports two local development workflows: #### Steps -1. Open the repository in VS Code. -2. Use the Dev Containers command to reopen the project in the devcontainer. -3. Let the post-create script finish installing Python and Node dependencies. -4. Confirm that the container has provisioned PostgreSQL and forwarded the main ports. +1. Open the repository in an editor with dev container support +2. Use the editor to reopen the project in the devcontainer + - For example, in VS Code, click the popup prompt or use the Command Palette to select "Dev Containers: Reopen in Container" +3. Let the container build from the root `compose.yaml` definition and finish running the post-create script +4. Confirm that the container has provisioned PostgreSQL and Keycloak, and forwarded the main ports + +The devcontainer now prepares local config files before API migrations/bootstrap and waits for PostgreSQL and Keycloak readiness before the post-create flow continues. +It also provisions project-local Python environments for both `api` and `notifications-api`, so the checked-in VS Code debug configurations work without extra manual setup. #### Expected Ports @@ -106,19 +79,18 @@ This repository supports two local development workflows: - `5002`: notifications API - `8080`: staff frontend - `8081`: appointment frontend +- `8085`: Keycloak auth server - `5432`: PostgreSQL -The devcontainer installs dependencies automatically, applies database migrations, and may initialize seed data depending on the current database state. +The devcontainer installs dependencies automatically for `api`, `notifications-api`, `frontend`, and `appointment-frontend`, applies database migrations, and may initialize seed data depending on the current database state. ### Local Host Workflow #### Prerequisites -- Python 3.14 -- `uv` -- Node.js 20 -- `npm` -- PostgreSQL 16 or a compatible local PostgreSQL instance +- Python 3.14 with `uv` +- Node.js 20 with `npm` +- PostgreSQL 16 #### Setup @@ -142,18 +114,14 @@ The devcontainer installs dependencies automatically, applies database migration npm install ``` -3. Create the required local config files from the repo's devcontainer config sources: +3. Create the required local config files: ```bash - cp ./.devcontainer/config/api/dotenv ./api/.env - cp ./.devcontainer/config/api/client_secrets/secrets.json ./api/client_secrets/secrets.json - cp ./.devcontainer/config/frontend/public/static/keycloak/keycloak.json ./frontend/public/static/keycloak/keycloak.json - cp ./.devcontainer/config/frontend/public/config/configuration.json ./frontend/public/config/configuration.json - cp ./.devcontainer/config/appointment-frontend/dotenv.local ./appointment-frontend/.env.local - cp ./.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json ./appointment-frontend/public/config/kc/keycloak-public.json - cp ./.devcontainer/config/appointment-frontend/public/config/configuration.json ./appointment-frontend/public/config/configuration.json + ./scripts/setup-local-config.sh ``` + This script copies the checked-in local config defaults from `.devcontainer/config`, creates missing destination directories, validates required API auth keys, and leaves any existing local files untouched. + 4. Start the local auth server: ```bash @@ -197,7 +165,7 @@ Queue management API using the production Dockerfile through Compose: docker compose --profile api up --build api ``` -The optional `api` Compose service still serves the application on `http://localhost:5000`. It reads `api/.env`, then overrides container-only settings so it can reach the host PostgreSQL and host-run notifications API while continuing to use the local Keycloak on `http://localhost:8085/auth`. +The root `compose.yaml` is the single source of truth for local Docker services and the devcontainer. The optional `api` Compose service still serves the application on `http://localhost:5000`. It reads `api/.env`, then overrides container-only settings so it can reach the host PostgreSQL and host-run notifications API while continuing to use the local Keycloak on `http://localhost:8085/auth`. Notifications API: @@ -234,6 +202,116 @@ These are the main local files you should expect to have in place when running t The checked-in local auth defaults now target the local Keycloak realm on `http://localhost:8085/auth`. If you need to switch back to the shared dev Keycloak server, update the copied local config files before starting the apps. +## Testing + +This repository includes Python/pytest suites, Artillery-based load tests, and Postman/Newman collections. The commands below assume you already completed the local setup for the relevant service and, for API-backed tests, have the local stack running. + +### Pytest Suites + +The main application test suite lives in `api/app/tests` and is split into a DB-free `smoke` slice and a Postgres-backed `integration` slice. + +From `api`: + +```bash +./scripts/run_api_smoke_tests.sh +./scripts/run_api_integration_tests.sh +./scripts/run_api_full_tests.sh +``` + +Equivalent direct pytest commands: + +```bash +uv run pytest app/tests -m smoke -q --override-ini "addopts=--strict-markers" +uv run pytest app/tests -m integration -q --override-ini "addopts=--strict-markers" --require-integration-db +uv run pytest app/tests -q --require-integration-db +``` + +Additional pytest suites: + +```bash +cd ./notifications-api +uv sync --group dev +uv run pytest +``` + +```bash +cd ./feedback-api +make setup +make test +``` + +### Load Tests + +Load testing lives in `tests/loadtesting` and uses Artillery against the local API stack. + +Initial setup: + +```bash +cd ./tests/loadtesting +npm install +cp envs.example.sh envs.sh +chmod +x envs.sh profile-python.sh +``` + +Before running the load tests locally, start the local Keycloak realm and seed the API data: + +```bash +docker compose up -d keycloak + +cd ./api +uv run python manage.py db upgrade +uv run python manage.py bootstrap +``` + +Run the load suites from `tests/loadtesting`: + +```bash +npm run tests:all +npm run tests:http +npm run tests:socket +``` + +Optional Python profiling commands are also available there: + +```bash +npm run python:profile +npm run python:top +``` + +### Newman Tests + +Postman collections live in `api/postman`. They target the local API and local Keycloak realm, and the checked-in local setup expects the demo users from `keycloak-local/servicebc-local-realm.json`. + +Before running Newman locally, make sure the API database is migrated and bootstrapped: + +```bash +cd ./api +uv run python manage.py db upgrade +uv run python manage.py bootstrap +``` + +Then install Newman and run the main collection from `api/postman`: + +```bash +cd ./api/postman +npm install +./node_modules/newman/bin/newman.js run API_Test_TheQ_Booking.json -e postman_env.json --bail failure \ + --global-var userid=cfms-postman-operator \ + --global-var password=password \ + --global-var userid_nonqtxn=cfms-postman-non-operator \ + --global-var password_nonqtxn=password \ + --global-var client_secret=theq-local-dev-secret \ + --global-var url=http://localhost:5000/api/v1/ \ + --global-var auth_url=http://localhost:8085 \ + --global-var clientid=theq-queue-management-api \ + --global-var realm=servicebc-local \ + --global-var public_url=http://localhost:5000/api/v1/ \ + --global-var public_user_id=cfms-postman-public-user \ + --global-var public_user_password=password +``` + +See `api/postman/README-local-auth.md` for local auth troubleshooting details. + ## Deployment For deployment within the B.C. government, this project can be hosted on the [B.C. government Private Cloud](https://digital.gov.bc.ca/technology/cloud/private/). The platform is the B.C. Government Private Cloud PaaS, powered by Red Hat OpenShift, and is designed for hosting government applications in a managed private-cloud environment. diff --git a/appointment-frontend/package-lock.json b/appointment-frontend/package-lock.json index 3c291e21e..fc12e51b5 100644 --- a/appointment-frontend/package-lock.json +++ b/appointment-frontend/package-lock.json @@ -31,7 +31,7 @@ "eslint-plugin-import": "^2.25.4", "vue": "^2.6.14", "@typescript-eslint/eslint-plugin": "^4.33.0", - "keycloak-js": "^20.0.0", + "keycloak-js": "^24.0.0", "@mdi/font": "^5.9.55", "eslint-plugin-node": "^11.1.0", "regenerator-runtime": "^0.13.9", @@ -104,12 +104,6 @@ "tslib": "^2.0.3" } }, - "node_modules/yorkie/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, "node_modules/foreground-child": { "version": "3.3.0", "dev": true, @@ -508,33 +502,6 @@ "read-pkg-up": "^7.0.1" } }, - "node_modules/cypress/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/progress-webpack-plugin/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/parse-json": { "version": "5.2.0", "dev": true, @@ -726,12 +693,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@vue/vue2-jest/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "dev": true, @@ -1104,6 +1065,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/is-fullwidth-code-point": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "license": "MIT", "engines": { @@ -1364,18 +1327,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/jest-changed-files/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/which-builtin-type": { "version": "1.2.0", "license": "MIT", @@ -2568,6 +2519,8 @@ }, "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", "dependencies": { @@ -2714,6 +2667,8 @@ }, "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", "dependencies": { @@ -2780,6 +2735,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/strip-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "license": "MIT", "dependencies": { @@ -2924,19 +2881,6 @@ "node": ">=8" } }, - "node_modules/nightwatch/node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/execa/node_modules/cross-spawn": { "version": "6.0.6", "dev": true, @@ -3003,12 +2947,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, "node_modules/launchdarkly-js-sdk-common/node_modules/uuid": { "version": "3.4.0", "license": "MIT", @@ -3144,15 +3082,6 @@ "dev": true, "license": "MIT" }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/is-bigint": { "version": "1.0.4", "license": "MIT", @@ -3411,18 +3340,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/vue-eslint-parser": { "version": "7.11.0", "dev": true, @@ -3631,13 +3548,6 @@ "node": ">= 0.6" } }, - "node_modules/tcp-port-used/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "peer": true - }, "node_modules/jest-resolve/node_modules/slash": { "version": "3.0.0", "dev": true, @@ -4240,6 +4150,8 @@ }, "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, @@ -4560,12 +4472,6 @@ "hasInstallScript": true, "license": "MIT" }, - "node_modules/stylus/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/error-stack-parser": { "version": "2.1.4", "dev": true, @@ -4974,15 +4880,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-code-frame/node_modules/ansi-regex": { "version": "2.1.1", "dev": true, @@ -5008,26 +4905,6 @@ "node": ">=6.9.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/jest-config/node_modules/slash": { "version": "3.0.0", "dev": true, @@ -5209,6 +5086,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/ansi-escapes": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true, "license": "MIT", "engines": { @@ -5367,6 +5246,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/wrap-ansi": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5858,15 +5739,6 @@ "punycode": "^2.3.1" } }, - "node_modules/progress-webpack-plugin/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, "node_modules/p-try": { "version": "2.2.0", "dev": true, @@ -5904,6 +5776,8 @@ }, "node_modules/yorkie/node_modules/get-stream": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, "license": "MIT", "engines": { @@ -6071,21 +5945,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/mkdirp": { "version": "0.5.6", "dev": true, @@ -6420,6 +6279,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/ansi-regex": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "license": "MIT", "engines": { @@ -7163,15 +7024,6 @@ "dev": true, "license": "MIT" }, - "node_modules/yorkie/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/postcss-colormin": { "version": "5.3.1", "dev": true, @@ -7346,12 +7198,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/html-tags": { "version": "3.3.1", "dev": true, @@ -7860,12 +7706,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-core/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/@types/serve-static": { "version": "1.15.7", "dev": true, @@ -7964,7 +7804,9 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -8484,6 +8326,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/restore-cursor": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8549,15 +8393,6 @@ "dev": true, "license": "MIT" }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", "license": "MIT", @@ -8990,7 +8825,9 @@ "license": "MIT" }, "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -9004,21 +8841,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nightwatch/node_modules/parse5": { "version": "6.0.1", "dev": true, @@ -9155,15 +8977,6 @@ "webpack": ">=2" } }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/stacktrace-parser": { "version": "0.1.10", "dev": true, @@ -9838,11 +9651,13 @@ } }, "node_modules/keycloak-js": { - "version": "20.0.5", + "version": "24.0.5", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.5.tgz", + "integrity": "sha512-VQOSn3j13DPB6OuavKAq+sRjDERhIKrXgBzekoHRstifPuyULILguugX6yxRUYFSpn3OMYUXmSX++tkdCupOjA==", "license": "Apache-2.0", "dependencies": { - "base64-js": "^1.5.1", - "js-sha256": "^0.9.0" + "js-sha256": "^0.11.0", + "jwt-decode": "^4.0.0" } }, "node_modules/babel-template": { @@ -10102,6 +9917,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/keycloak-js/node_modules/js-sha256": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz", + "integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==", + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.2", "dev": true, @@ -10123,12 +9944,6 @@ "dev": true, "license": "MIT" }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/lodash.uniqueid": { "version": "4.0.1", "license": "MIT" @@ -10243,12 +10058,6 @@ "source-map-js": "^1.0.2" } }, - "node_modules/progress-webpack-plugin/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, "node_modules/lodash.uniq": { "version": "4.5.0", "dev": true, @@ -10730,6 +10539,8 @@ }, "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, @@ -11039,6 +10850,8 @@ }, "node_modules/mocha/node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, @@ -11830,6 +11643,15 @@ "source-map": "~0.6.1" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/pac-resolver": { "version": "7.0.1", "dev": true, @@ -12607,15 +12429,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-vue/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ansi-to-html/node_modules/entities": { "version": "2.2.0", "dev": true, @@ -12897,12 +12710,6 @@ } } }, - "node_modules/puppeteer/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/htmlparser2": { "version": "6.1.0", "dev": true, @@ -13752,14 +13559,6 @@ "version": "2.0.1", "license": "MIT" }, - "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/workbox-build/node_modules/source-map": { "version": "0.8.0-beta.0", "dev": true, @@ -14507,18 +14306,6 @@ "node": ">=8" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/workbox-range-requests": { "version": "6.6.0", "dev": true, @@ -14587,21 +14374,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-runtime/node_modules/slash": { "version": "3.0.0", "dev": true, @@ -14759,12 +14531,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "license": "MIT", @@ -14979,17 +14745,6 @@ "node": ">=8" } }, - "node_modules/yorkie/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "node_modules/cypress": { "version": "12.17.4", "dev": true, @@ -15691,6 +15446,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/mimic-fn": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, "license": "MIT", "engines": { @@ -16597,6 +16354,8 @@ }, "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, @@ -16623,6 +16382,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/onetime": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16709,16 +16470,6 @@ "node": ">=8" } }, - "node_modules/yorkie/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "node_modules/sinon/node_modules/diff": { "version": "4.0.2", "dev": true, @@ -16759,12 +16510,6 @@ } } }, - "node_modules/babel-traverse/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/socks-proxy-agent": { "version": "8.0.4", "dev": true, @@ -16960,6 +16705,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/cli-cursor": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, "license": "MIT", "dependencies": { @@ -18167,6 +17914,8 @@ }, "node_modules/progress-webpack-plugin/node_modules/string-width": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "license": "MIT", "dependencies": { @@ -18523,18 +18272,6 @@ "@babel/highlight": "^7.10.4" } }, - "node_modules/yorkie/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "dev": true, @@ -18851,21 +18588,6 @@ "version": "6.19.8", "license": "MIT" }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/css-parse": { "version": "2.0.0", "dev": true, @@ -18929,18 +18651,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yorkie/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-data-view": { "version": "1.0.1", "license": "MIT", @@ -19056,12 +18766,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@vue/component-compiler-utils/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "license": "MIT", diff --git a/compose.yaml b/compose.yaml index e9c407c65..a5c474004 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,4 +1,65 @@ services: + app: + build: + args: + VARIANT: "3.14-bookworm" + NODE_VERSION: "20" + context: . + dockerfile: .devcontainer/Dockerfile + command: sleep infinity + environment: + UV_PROJECT_ENVIRONMENT: /workspace/api/.venv + DATABASE_NAME: postgres + DATABASE_PASSWORD: postgres + DISPLAY: ":14" + LIBGL_ALWAYS_INDIRECT: "0" + JWT_OIDC_WELL_KNOWN_CONFIG: "" + JWT_OIDC_JWKS_URI: http://keycloak:8080/auth/realms/servicebc-local/protocol/openid-connect/certs + JWT_OIDC_ISSUER: http://localhost:8085/auth/realms/servicebc-local + init: true + network_mode: service:db + depends_on: + db: + condition: service_healthy + keycloak: + condition: service_healthy + volumes: + - x11:/tmp/.X11-unix:rw + - .:/workspace:cached + - api-venv:/workspace/api/.venv + - notifications-api-venv:/workspace/notifications-api/.venv + - appointment-frontend-node-modules:/workspace/appointment-frontend/node_modules + - frontend-node-modules:/workspace/frontend/node_modules + + db: + environment: + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 5s + image: postgres:16.2 + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + + x11: + environment: + DISPLAY: ":14" + MODE: tcp + XPRA_HTML: "yes" + XPRA_PASSWORD: password + image: jare/x11-bridge + ports: + - "10000:10000" + restart: always + volumes: + - x11:/tmp/.X11-unix:rw + keycloak: image: quay.io/keycloak/keycloak:26.2.2 command: @@ -10,6 +71,16 @@ services: KC_HEALTH_ENABLED: "true" KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: password + healthcheck: + test: + [ + "CMD-SHELL", + "exec 3<>/dev/tcp/127.0.0.1/8080; echo -e 'GET /auth/realms/servicebc-local/.well-known/openid-configuration HTTP/1.1\\r\\nhost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3; grep 'HTTP/1.1 200 OK' <&3", + ] + interval: 5s + timeout: 5s + retries: 20 + start_period: 20s ports: - "8085:8080" volumes: @@ -38,3 +109,11 @@ services: - host.docker.internal:host-gateway ports: - "5000:8080" + +volumes: + postgres-data: + x11: + api-venv: + notifications-api-venv: + appointment-frontend-node-modules: + frontend-node-modules: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 466302082..052720df7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,7 +28,7 @@ "es6-promise": "^4.2.8", "es6-shim": "^0.35.6", "js-file-download": "^0.4.12", - "keycloak-js": "^20.0.0", + "keycloak-js": "^24.0.0", "lodash": "^4.17.19", "luxon": "^1.28.1", "moment": "^2.29.4", @@ -164,17 +164,25 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, "node_modules/@babel/compat-data": { "version": "7.25.2", "license": "MIT", @@ -214,6 +222,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -229,13 +238,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -366,6 +377,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.24.8", "license": "MIT", @@ -378,11 +398,13 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -474,14 +496,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -596,10 +622,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1730,39 +1758,45 @@ "license": "MIT" }, "node_modules/@babel/template": { - "version": "7.25.9", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.3", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.3", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1812,13 +1846,16 @@ } }, "node_modules/@cypress/request/node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -1826,10 +1863,11 @@ } }, "node_modules/@cypress/request/node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" }, @@ -2052,7 +2090,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -2063,7 +2103,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -2095,11 +2137,13 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -2304,7 +2348,9 @@ } }, "node_modules/@jest/expect/node_modules/@types/yargs": { - "version": "17.0.33", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -2511,7 +2557,9 @@ "license": "MIT" }, "node_modules/@jest/expect/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -2717,15 +2765,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -2735,13 +2781,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.6", "license": "MIT", @@ -2751,11 +2790,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2921,7 +2964,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.27.8", + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", "dev": true, "license": "MIT" }, @@ -3322,7 +3367,9 @@ } }, "node_modules/@types/yargs": { - "version": "16.0.9", + "version": "16.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.11.tgz", + "integrity": "sha512-sbtvk8wDN+JvEdabmZExoW/HNr1cB7D/j4LT08rMiuikfA7m/JNJg7ATQcgzs34zHnoScDkY0ZRSl29Fkmk36g==", "dev": true, "license": "MIT", "dependencies": { @@ -3757,7 +3804,9 @@ } }, "node_modules/@vue/cli-plugin-babel/node_modules/@vue/babel-preset-app": { - "version": "5.0.8", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-5.0.9.tgz", + "integrity": "sha512-0rKOF4s/AhaRMJLybxOCgXfwtYhO3pwDSL/q/W8wRs1LzmHAc77FyTXWlun6VyKiSKwSdtH7CvOiWqq+DfofdA==", "dev": true, "license": "MIT", "dependencies": { @@ -3992,7 +4041,9 @@ } }, "node_modules/@vue/cli-service/node_modules/autoprefixer": { - "version": "10.4.20", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", "dev": true, "funding": [ { @@ -4010,11 +4061,10 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -4042,6 +4092,13 @@ "node": ">=10" } }, + "node_modules/@vue/cli-service/node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/@vue/cli-service/node_modules/postcss-loader": { "version": "6.2.1", "dev": true, @@ -4135,45 +4192,53 @@ "license": "ISC" }, "node_modules/@vue/compiler-core": { - "version": "3.5.13", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.33.tgz", + "integrity": "sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.13", - "entities": "^4.5.0", + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.33", + "entities": "^7.0.1", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.13", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.33.tgz", + "integrity": "sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-core": "3.5.33", + "@vue/shared": "3.5.33" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.13", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.33.tgz", + "integrity": "sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.13", - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13", + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.33", + "@vue/compiler-dom": "3.5.33", + "@vue/compiler-ssr": "3.5.33", + "@vue/shared": "3.5.33", "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.48", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.21", + "postcss": "^8.5.10", + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.13", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.33.tgz", + "integrity": "sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-dom": "3.5.33", + "@vue/shared": "3.5.33" } }, "node_modules/@vue/component-compiler-utils": { @@ -4212,7 +4277,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vue/component-compiler-utils/node_modules/postcss": { "version": "7.0.39", @@ -4234,7 +4300,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vue/eslint-config-standard": { "version": "6.1.0", @@ -4277,7 +4344,9 @@ } }, "node_modules/@vue/shared": { - "version": "3.5.13", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.33.tgz", + "integrity": "sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -4332,6 +4401,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -4402,7 +4472,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@vue/vue2-jest/node_modules/escape-string-regexp": { "version": "1.0.5", @@ -4599,7 +4670,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4652,7 +4725,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.4", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, "license": "MIT", "dependencies": { @@ -4712,7 +4787,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -4742,7 +4819,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -4901,12 +4980,14 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -5006,14 +5087,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5023,18 +5106,19 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -5069,7 +5153,9 @@ } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/assert-plus": { @@ -5100,6 +5186,16 @@ "node": "*" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "dev": true, @@ -5282,6 +5378,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5497,6 +5594,18 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz", + "integrity": "sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "dev": true, @@ -5586,7 +5695,9 @@ "license": "MIT" }, "node_modules/bn.js": { - "version": "5.2.1", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", "license": "MIT" }, "node_modules/body-parser": { @@ -5709,7 +5820,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5805,7 +5918,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "funding": [ { "type": "opencollective", @@ -5822,10 +5937,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -5985,14 +6101,44 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -6099,7 +6245,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", "funding": [ { "type": "opencollective", @@ -6506,7 +6654,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/compression/node_modules/safe-buffer": { "version": "5.1.2", @@ -6668,7 +6817,9 @@ } }, "node_modules/core-js": { - "version": "3.39.0", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6724,7 +6875,9 @@ } }, "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/create-hash": { @@ -6929,7 +7082,9 @@ } }, "node_modules/create-jest/node_modules/@types/yargs": { - "version": "17.0.33", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -6997,7 +7152,9 @@ } }, "node_modules/create-jest/node_modules/dedent": { - "version": "1.5.3", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7488,7 +7645,9 @@ } }, "node_modules/create-jest/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -7629,7 +7788,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -7677,7 +7838,9 @@ } }, "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -7706,13 +7869,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7996,9 +8161,9 @@ } }, "node_modules/cypress/node_modules/ci-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", - "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -8006,6 +8171,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -8037,6 +8203,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -8070,7 +8237,9 @@ } }, "node_modules/cypress/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -8130,13 +8299,15 @@ } }, "node_modules/data-view-buffer": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8146,27 +8317,31 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -8503,7 +8678,9 @@ } }, "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/dir-glob": { @@ -8710,6 +8887,20 @@ "crossvent": "1.5.5" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "dev": true, @@ -8767,7 +8958,9 @@ } }, "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.1", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", "dependencies": { @@ -8797,7 +8990,9 @@ } }, "node_modules/editorconfig/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -8813,7 +9008,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.12", + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", "license": "ISC" }, "node_modules/elliptic": { @@ -8830,7 +9027,9 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/emittery": { @@ -8954,7 +9153,9 @@ } }, "node_modules/entities": { - "version": "4.5.0", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -8993,56 +9194,66 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", + "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -9052,11 +9263,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -9073,8 +9283,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -9084,13 +9295,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9105,13 +9319,15 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -9170,7 +9386,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -9452,17 +9670,25 @@ } }, "node_modules/eslint-import-resolver-webpack/node_modules/resolve": { - "version": "2.0.0-next.5", + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9739,6 +9965,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=4" } @@ -9817,6 +10044,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=4" } @@ -9889,10 +10117,11 @@ } }, "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -9909,6 +10138,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -9933,13 +10163,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -10144,6 +10376,8 @@ }, "node_modules/estree-walker": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, "node_modules/esutils": { @@ -10277,6 +10511,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10381,7 +10616,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/express/node_modules/qs": { "version": "6.13.0", @@ -10752,10 +10988,18 @@ } }, "node_modules/for-each": { - "version": "0.3.3", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -10856,13 +11100,17 @@ } }, "node_modules/form-data": { - "version": "3.0.2", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -10932,14 +11180,16 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -11011,14 +11261,18 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -11034,6 +11288,8 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", "funding": { @@ -11093,14 +11349,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11117,6 +11380,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stdin": { "version": "5.0.1", "dev": true, @@ -11137,13 +11413,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -11300,10 +11578,12 @@ "license": "MIT" }, "node_modules/gopd": { - "version": "1.0.1", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11368,9 +11648,14 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11393,8 +11678,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -11403,7 +11694,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -11628,11 +11921,17 @@ } }, "node_modules/html-webpack-plugin/node_modules/tapable": { - "version": "2.2.1", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/htmlparser2": { @@ -11960,13 +12259,15 @@ "license": "MIT" }, "node_modules/internal-slot": { - "version": "1.0.7", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -12024,12 +12325,15 @@ "license": "MIT" }, "node_modules/is-array-buffer": { - "version": "3.0.4", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -12043,12 +12347,37 @@ "dev": true, "license": "MIT" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { - "version": "1.0.4", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12066,12 +12395,14 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12096,7 +12427,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -12109,10 +12442,14 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -12123,11 +12460,14 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12173,6 +12513,22 @@ "read-pkg-up": "^7.0.1" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, @@ -12245,6 +12601,19 @@ "dev": true, "license": "MIT" }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.3", "dev": true, @@ -12264,11 +12633,14 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12313,12 +12685,16 @@ "license": "MIT" }, "node_modules/is-regex": { - "version": "1.1.4", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -12327,12 +12703,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -12350,11 +12741,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12364,11 +12758,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -12378,10 +12776,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -12405,12 +12805,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { - "version": "1.0.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12522,7 +12957,9 @@ } }, "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -12640,6 +13077,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -12985,7 +13423,9 @@ } }, "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "17.0.33", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -13066,7 +13506,9 @@ } }, "node_modules/jest-cli/node_modules/dedent": { - "version": "1.5.3", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -13660,7 +14102,9 @@ } }, "node_modules/jest-cli/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -14367,7 +14811,9 @@ "license": "MIT" }, "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.33", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -14379,6 +14825,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -14402,6 +14849,7 @@ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^28.1.3", @@ -14422,6 +14870,7 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -14496,6 +14945,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, + "license": "MIT", "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -14510,7 +14960,8 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jest-watch-typeahead/node_modules/slash": { "version": "4.0.0", @@ -14547,11 +14998,13 @@ } }, "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -14561,7 +15014,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -14696,7 +15151,9 @@ } }, "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", "dependencies": { @@ -14704,7 +15161,10 @@ } }, "node_modules/js-beautify/node_modules/glob": { - "version": "10.4.5", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -14723,11 +15183,13 @@ } }, "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.5", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -14737,9 +15199,11 @@ } }, "node_modules/js-beautify/node_modules/minipass": { - "version": "7.1.2", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -14779,7 +15243,9 @@ } }, "node_modules/js-sha256": { - "version": "0.9.0", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz", + "integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==", "license": "MIT" }, "node_modules/js-tokens": { @@ -14931,7 +15397,9 @@ } }, "node_modules/jsonfile": { - "version": "6.1.0", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -15014,11 +15482,22 @@ "license": "MIT" }, "node_modules/keycloak-js": { - "version": "20.0.5", + "version": "24.0.5", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.5.tgz", + "integrity": "sha512-VQOSn3j13DPB6OuavKAq+sRjDERhIKrXgBzekoHRstifPuyULILguugX6yxRUYFSpn3OMYUXmSX++tkdCupOjA==", "license": "Apache-2.0", "dependencies": { - "base64-js": "^1.5.1", - "js-sha256": "^0.9.0" + "js-sha256": "^0.11.0", + "jwt-decode": "^4.0.0" + } + }, + "node_modules/keycloak-js/node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" } }, "node_modules/keyv": { @@ -15171,6 +15650,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -15359,10 +15839,12 @@ "license": "Apache-2.0" }, "node_modules/magic-string": { - "version": "0.30.14", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-dir": { @@ -15463,6 +15945,15 @@ "version": "1.2.0", "license": "MIT" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5.js": { "version": "1.3.5", "license": "MIT", @@ -15542,6 +16033,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -15605,7 +16097,9 @@ } }, "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/mime": { @@ -15693,10 +16187,11 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15713,6 +16208,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -15724,13 +16220,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -15746,11 +16244,17 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/tapable": { - "version": "2.2.1", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/minimalistic-assert": { @@ -15762,7 +16266,9 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -16003,7 +16509,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.8", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -16061,6 +16569,35 @@ "tslib": "^2.0.3" } }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/node-fetch": { "version": "2.6.13", "license": "MIT", @@ -16198,7 +16735,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "license": "MIT" }, "node_modules/node-sass": { @@ -16419,7 +16958,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -16441,12 +16982,16 @@ "license": "MIT" }, "node_modules/object.assign": { - "version": "4.1.5", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -16456,6 +17001,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.fromentries": { "version": "2.0.8", "dev": true, @@ -16640,6 +17201,24 @@ "dev": true, "license": "MIT" }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-finally": { "version": "1.0.0", "dev": true, @@ -16882,7 +17461,9 @@ } }, "node_modules/patch-package/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16909,13 +17490,18 @@ } }, "node_modules/patch-package/node_modules/yaml": { - "version": "2.6.1", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/path-exists": { @@ -16969,9 +17555,11 @@ "license": "ISC" }, "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -17015,6 +17603,7 @@ }, "node_modules/picocolors": { "version": "1.0.1", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -17100,6 +17689,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -17113,6 +17703,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.0.0" }, @@ -17125,6 +17716,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -17200,7 +17792,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "funding": [ { "type": "opencollective", @@ -17217,7 +17811,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -17841,6 +18435,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -17850,6 +18445,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -17859,6 +18455,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -17884,6 +18481,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^2.0.0" }, @@ -17896,6 +18494,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -17904,7 +18503,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/progress-webpack-plugin/node_modules/escape-string-regexp": { "version": "1.0.5", @@ -17938,6 +18538,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -17960,6 +18561,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -17969,6 +18571,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" }, @@ -17981,6 +18584,7 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -17994,6 +18598,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, + "license": "MIT", "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -18007,6 +18612,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^3.0.0" }, @@ -18030,6 +18636,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0" @@ -18112,7 +18719,9 @@ } }, "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.1", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/pump": { @@ -18257,7 +18866,9 @@ } }, "node_modules/quill/node_modules/eventemitter3": { - "version": "5.0.1", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, "node_modules/randombytes": { @@ -18344,7 +18955,8 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", @@ -18434,6 +19046,29 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerate": { "version": "1.4.2", "license": "MIT" @@ -18460,14 +19095,18 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -18618,7 +19257,9 @@ } }, "node_modules/request/node_modules/qs": { - "version": "6.5.3", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -18670,16 +19311,22 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.8", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -18812,13 +19459,16 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -18852,14 +19502,33 @@ "rust-result": "^1.0.0" } }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -18911,6 +19580,7 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -19034,11 +19704,13 @@ } }, "node_modules/scss-tokenizer/node_modules/source-map": { - "version": "0.7.4", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/select-hose": { @@ -19181,6 +19853,7 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -19258,6 +19931,8 @@ }, "node_modules/set-function-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19270,6 +19945,21 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "dev": true, @@ -19339,14 +20029,73 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -19693,6 +20442,20 @@ "websocket": "latest" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-browserify": { "version": "3.0.0", "license": "MIT", @@ -19768,14 +20531,19 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -19785,14 +20553,20 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -19998,7 +20772,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -20330,7 +21106,9 @@ "dev": true }, "node_modules/tmp": { - "version": "0.2.3", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { @@ -20473,6 +21251,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -20485,6 +21264,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -20509,31 +21289,41 @@ } }, "node_modules/ts-loader/node_modules/enhanced-resolve": { - "version": "5.17.1", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/ts-loader/node_modules/tapable": { - "version": "2.2.1", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tsconfig-paths": { @@ -20649,28 +21439,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -20680,16 +21474,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -20699,16 +21496,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -20737,14 +21536,19 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -20854,7 +21658,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -20871,8 +21677,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -20881,6 +21687,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/update-browserslist-db/node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, "node_modules/uri-js": { "version": "4.4.1", "license": "BSD-2-Clause", @@ -21025,11 +21837,13 @@ "license": "MIT" }, "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/validate-npm-package-license": { @@ -21333,6 +22147,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -21392,6 +22207,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -21713,7 +22529,9 @@ } }, "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -21744,10 +22562,11 @@ "license": "MIT" }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -21821,7 +22640,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.17.1", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -21855,7 +22676,9 @@ } }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.7", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -21878,7 +22701,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.2.0", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "dev": true, "license": "MIT", "engines": { @@ -21891,10 +22716,11 @@ "license": "MIT" }, "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -21910,7 +22736,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.0", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "dev": true, "license": "MIT", "engines": { @@ -21955,11 +22783,13 @@ "license": "MIT" }, "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.17.1", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -21982,10 +22812,16 @@ } }, "node_modules/webpack/node_modules/tapable": { - "version": "2.2.1", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/websocket": { @@ -22083,28 +22919,84 @@ } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.15", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -22256,7 +23148,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "1.10.2", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "dev": true, "license": "ISC", "engines": { @@ -22285,6 +23179,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -22294,6 +23189,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -22343,6 +23239,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, + "license": "MIT", "dependencies": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -22371,6 +23268,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -22391,6 +23289,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, + "license": "ISC", "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -22409,6 +23308,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^1.0.0" }, @@ -22421,6 +23321,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -22438,6 +23339,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -22449,7 +23351,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "dev": true, + "license": "ISC" } } } diff --git a/scripts/setup-local-config.sh b/scripts/setup-local-config.sh new file mode 100755 index 000000000..804f5faee --- /dev/null +++ b/scripts/setup-local-config.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# Copyright 2022 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +set -u + +COLOR_DEFAULT='\033[0m' +COLOR_FAILURE='\033[0;31m' + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo_failure () { + echo -e "$COLOR_FAILURE$*$COLOR_DEFAULT" >&2 +} + +copy_config () { + SOURCE="$1" + DESTINATION="$2" + + if [ ! -f "$SOURCE" ]; then + echo_failure "Configuration source file $SOURCE is missing" + return 1 + fi + + if [ -f "$DESTINATION" ]; then + echo "Using pre-existing file $DESTINATION" + return 0 + fi + + DIRECTORY="$(dirname "$DESTINATION")" + if [ ! -d "$DIRECTORY" ]; then + echo "Creating directory $DIRECTORY" + mkdir -p "$DIRECTORY" + fi + + echo "Copying $SOURCE to $DESTINATION" + cp "$SOURCE" "$DESTINATION" +} + +check_setting () { + FILENAME="$1" + KEY_NAME="$2" + + if ! grep "$KEY_NAME" "$FILENAME" > /dev/null; then + echo_failure "Missing configuration key $KEY_NAME in $FILENAME" + return 1 + fi +} + +main () { + cd "$REPO_ROOT" || exit 1 + + copy_config "$REPO_ROOT/.devcontainer/config/api/dotenv" \ + "$REPO_ROOT/api/.env" + check_setting "$REPO_ROOT/api/.env" JWT_OIDC_AUDIENCE + check_setting "$REPO_ROOT/api/.env" JWT_OIDC_WELL_KNOWN_CONFIG + + copy_config "$REPO_ROOT/.devcontainer/config/api/client_secrets/secrets.json" \ + "$REPO_ROOT/api/client_secrets/secrets.json" + + copy_config "$REPO_ROOT/.devcontainer/config/frontend/public/static/keycloak/keycloak.json" \ + "$REPO_ROOT/frontend/public/static/keycloak/keycloak.json" + + copy_config "$REPO_ROOT/.devcontainer/config/frontend/public/config/configuration.json" \ + "$REPO_ROOT/frontend/public/config/configuration.json" + + copy_config "$REPO_ROOT/.devcontainer/config/appointment-frontend/dotenv.local" \ + "$REPO_ROOT/appointment-frontend/.env.local" + + copy_config "$REPO_ROOT/.devcontainer/config/appointment-frontend/public/config/kc/keycloak-public.json" \ + "$REPO_ROOT/appointment-frontend/public/config/kc/keycloak-public.json" + + copy_config "$REPO_ROOT/.devcontainer/config/appointment-frontend/public/config/configuration.json" \ + "$REPO_ROOT/appointment-frontend/public/config/configuration.json" +} + +main "$@" From a1b6767d3dae52e4af8103368530ce0621ecc7d9 Mon Sep 17 00:00:00 2001 From: Chris Sampson Date: Mon, 27 Apr 2026 22:16:07 -0700 Subject: [PATCH 46/81] Add OIDC config error handling for notifications-api --- notifications-api/src/api/__init__.py | 18 ++++- notifications-api/src/api/app_config.py | 2 + notifications-api/tests/conftest.py | 2 + notifications-api/tests/test_config.py | 88 +++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/notifications-api/src/api/__init__.py b/notifications-api/src/api/__init__.py index b94cfd2bb..7fb46870d 100644 --- a/notifications-api/src/api/__init__.py +++ b/notifications-api/src/api/__init__.py @@ -14,6 +14,7 @@ """Flask application factory.""" import os +from urllib.error import URLError from flask import Flask @@ -46,7 +47,22 @@ def get_roles(a_dict): return a_dict["realm_access"]["roles"] # pragma: no cover app.config["JWT_ROLE_CALLBACK"] = get_roles - jwt_manager.init_app(app) + try: + jwt_manager.init_app(app) + except URLError: + well_known_config = app.config.get("JWT_OIDC_WELL_KNOWN_CONFIG") + jwks_uri = app.config.get("JWT_OIDC_JWKS_URI") + issuer = app.config.get("JWT_OIDC_ISSUER") + + if not (well_known_config and jwks_uri and issuer): + raise + + app.logger.warning( + "JWT well-known config could not be reached; retrying with direct JWKS and issuer settings." + ) + app.config["JWT_OIDC_WELL_KNOWN_CONFIG"] = None + jwt_manager.init_app(app) + app.config["JWT_OIDC_WELL_KNOWN_CONFIG"] = well_known_config def register_shellcontext(app): diff --git a/notifications-api/src/api/app_config.py b/notifications-api/src/api/app_config.py index 1fc0fa2cc..fd4fb2f2e 100644 --- a/notifications-api/src/api/app_config.py +++ b/notifications-api/src/api/app_config.py @@ -66,6 +66,8 @@ class _Config: # pylint: disable=too-few-public-methods SECRET_KEY = os.getenv("SECRET_KEY") JWT_OIDC_WELL_KNOWN_CONFIG = os.getenv("JWT_OIDC_WELL_KNOWN_CONFIG") + JWT_OIDC_JWKS_URI = os.getenv("JWT_OIDC_JWKS_URI") + JWT_OIDC_ISSUER = os.getenv("JWT_OIDC_ISSUER") JWT_OIDC_ALGORITHMS = os.getenv("JWT_OIDC_ALGORITHMS", "RS256") JWT_OIDC_AUDIENCE = os.getenv("JWT_OIDC_AUDIENCE") JWT_OIDC_CLIENT_SECRET = os.getenv("JWT_OIDC_CLIENT_SECRET", "") diff --git a/notifications-api/tests/conftest.py b/notifications-api/tests/conftest.py index ce3234583..aeef39ca0 100644 --- a/notifications-api/tests/conftest.py +++ b/notifications-api/tests/conftest.py @@ -22,6 +22,8 @@ def app(monkeypatch): "JWT_OIDC_WELL_KNOWN_CONFIG", "https://example.com/.well-known/openid-configuration", ) + monkeypatch.setenv("JWT_OIDC_JWKS_URI", "https://example.com/jwks.json") + monkeypatch.setenv("JWT_OIDC_ISSUER", "https://example.com/") monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") monkeypatch.setenv("GC_NOTIFY_API_KEY", "test-key") monkeypatch.setenv("GC_NOTIFY_API_BASE_URL", "https://api.notification.canada.ca/") diff --git a/notifications-api/tests/test_config.py b/notifications-api/tests/test_config.py index 93747aad0..acbb41f03 100644 --- a/notifications-api/tests/test_config.py +++ b/notifications-api/tests/test_config.py @@ -1,4 +1,7 @@ import importlib +from urllib.error import URLError + +import pytest def test_create_app_prefers_flask_configuration(monkeypatch): @@ -19,6 +22,8 @@ def test_create_app_prefers_flask_configuration(monkeypatch): "JWT_OIDC_WELL_KNOWN_CONFIG", "https://example.com/.well-known/openid-configuration", ) + monkeypatch.setenv("JWT_OIDC_JWKS_URI", "https://example.com/jwks.json") + monkeypatch.setenv("JWT_OIDC_ISSUER", "https://example.com/") monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") import api @@ -32,6 +37,89 @@ def test_create_app_prefers_flask_configuration(monkeypatch): assert app.config["TESTING"] is True +def test_create_app_falls_back_to_direct_jwks_when_well_known_unreachable(monkeypatch): + calls = [] + + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.requires_auth", + lambda self, func: func, + raising=False, + ) + + def fake_init_app(self, app): + calls.append(app.config.get("JWT_OIDC_WELL_KNOWN_CONFIG")) + if len(calls) == 1: + raise URLError("connection refused") + + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.init_app", + fake_init_app, + raising=False, + ) + monkeypatch.setenv("FLASK_CONFIGURATION", "testing") + monkeypatch.setenv("SECRET_KEY", "test-secret") + monkeypatch.setenv( + "JWT_OIDC_WELL_KNOWN_CONFIG", + "http://localhost:8085/auth/realms/servicebc-local/.well-known/openid-configuration", + ) + monkeypatch.setenv( + "JWT_OIDC_JWKS_URI", + "http://keycloak:8080/auth/realms/servicebc-local/protocol/openid-connect/certs", + ) + monkeypatch.setenv( + "JWT_OIDC_ISSUER", + "http://localhost:8085/auth/realms/servicebc-local", + ) + monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") + + import api + import api.app_config as config + + importlib.reload(config) + importlib.reload(api) + + app = api.create_app() + + assert app.config["JWT_OIDC_WELL_KNOWN_CONFIG"] == ( + "http://localhost:8085/auth/realms/servicebc-local/.well-known/openid-configuration" + ) + assert calls == [ + "http://localhost:8085/auth/realms/servicebc-local/.well-known/openid-configuration", + None, + ] + + +def test_create_app_raises_when_well_known_unreachable_without_fallback(monkeypatch): + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.requires_auth", + lambda self, func: func, + raising=False, + ) + monkeypatch.setattr( + "flask_jwt_oidc.JwtManager.init_app", + lambda self, app: (_ for _ in ()).throw(URLError("connection refused")), + raising=False, + ) + monkeypatch.setenv("FLASK_CONFIGURATION", "testing") + monkeypatch.setenv("SECRET_KEY", "test-secret") + monkeypatch.setenv( + "JWT_OIDC_WELL_KNOWN_CONFIG", + "http://localhost:8085/auth/realms/servicebc-local/.well-known/openid-configuration", + ) + monkeypatch.delenv("JWT_OIDC_JWKS_URI", raising=False) + monkeypatch.delenv("JWT_OIDC_ISSUER", raising=False) + monkeypatch.setenv("JWT_OIDC_AUDIENCE", "theq-notifications-api") + + import api + import api.app_config as config + + importlib.reload(config) + importlib.reload(api) + + with pytest.raises(URLError): + api.create_app() + + def test_sms_provider_uses_app_config(app): from api.services.sms import get_sms_service from api.services.sms.custom_notify import CustomNotify From 47ae9e7a69842a90e9a8bb44731033684e536f06 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 14:51:40 -0700 Subject: [PATCH 47/81] dev-44: scaffold React + TypeScript boilerplate with ESLint, Prettier, strict TS, and path aliases --- appointment-booking/.gitignore | 24 + appointment-booking/.prettierrc | 7 + appointment-booking/README.md | 73 + appointment-booking/eslint.config.js | 31 + appointment-booking/index.html | 13 + appointment-booking/package-lock.json | 4856 ++++++++++++++++++++++ appointment-booking/package.json | 39 + appointment-booking/public/favicon.svg | 1 + appointment-booking/public/icons.svg | 24 + appointment-booking/src/App.css | 184 + appointment-booking/src/App.tsx | 102 + appointment-booking/src/assets/hero.png | Bin 0 -> 13057 bytes appointment-booking/src/assets/react.svg | 1 + appointment-booking/src/assets/vite.svg | 1 + appointment-booking/src/index.css | 111 + appointment-booking/src/main.tsx | 10 + appointment-booking/tsconfig.app.json | 37 + appointment-booking/tsconfig.json | 7 + appointment-booking/tsconfig.node.json | 24 + appointment-booking/vite.config.ts | 13 + 20 files changed, 5558 insertions(+) create mode 100644 appointment-booking/.gitignore create mode 100644 appointment-booking/.prettierrc create mode 100644 appointment-booking/README.md create mode 100644 appointment-booking/eslint.config.js create mode 100644 appointment-booking/index.html create mode 100644 appointment-booking/package-lock.json create mode 100644 appointment-booking/package.json create mode 100644 appointment-booking/public/favicon.svg create mode 100644 appointment-booking/public/icons.svg create mode 100644 appointment-booking/src/App.css create mode 100644 appointment-booking/src/App.tsx create mode 100644 appointment-booking/src/assets/hero.png create mode 100644 appointment-booking/src/assets/react.svg create mode 100644 appointment-booking/src/assets/vite.svg create mode 100644 appointment-booking/src/index.css create mode 100644 appointment-booking/src/main.tsx create mode 100644 appointment-booking/tsconfig.app.json create mode 100644 appointment-booking/tsconfig.json create mode 100644 appointment-booking/tsconfig.node.json create mode 100644 appointment-booking/vite.config.ts diff --git a/appointment-booking/.gitignore b/appointment-booking/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/appointment-booking/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/appointment-booking/.prettierrc b/appointment-booking/.prettierrc new file mode 100644 index 000000000..c03d068ab --- /dev/null +++ b/appointment-booking/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2 +} diff --git a/appointment-booking/README.md b/appointment-booking/README.md new file mode 100644 index 000000000..7dbf7ebf3 --- /dev/null +++ b/appointment-booking/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/appointment-booking/eslint.config.js b/appointment-booking/eslint.config.js new file mode 100644 index 000000000..4d44fdae7 --- /dev/null +++ b/appointment-booking/eslint.config.js @@ -0,0 +1,31 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import prettier from 'eslint-config-prettier' +import prettierPlugin from 'eslint-plugin-prettier' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + prettier, + ], + plugins: { + prettier: prettierPlugin, + }, + languageOptions: { + globals: globals.browser, + }, + rules: { + 'prettier/prettier': 'error', + }, + }, +]) diff --git a/appointment-booking/index.html b/appointment-booking/index.html new file mode 100644 index 000000000..84fad34e0 --- /dev/null +++ b/appointment-booking/index.html @@ -0,0 +1,13 @@ + + + + + + + appointment-booking + + +
+ + + diff --git a/appointment-booking/package-lock.json b/appointment-booking/package-lock.json new file mode 100644 index 000000000..ee997d23b --- /dev/null +++ b/appointment-booking/package-lock.json @@ -0,0 +1,4856 @@ +{ + "name": "appointment-booking", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "appointment-booking", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.6", + "react-dom": "^19.2.6" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@typescript-eslint/eslint-plugin": "^8.59.3", + "@typescript-eslint/parser": "^8.59.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.5", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "prettier": "^3.8.3", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.129.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz", + "integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", + "integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz", + "integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz", + "integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz", + "integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz", + "integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz", + "integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz", + "integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz", + "integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz", + "integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz", + "integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz", + "integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz", + "integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz", + "integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz", + "integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz", + "integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", + "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/type-utils": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.3", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", + "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", + "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", + "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", + "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.3", + "@typescript-eslint/tsconfig-utils": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", + "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", + "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.354", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.354.tgz", + "integrity": "sha512-JaBHwWcfIdmSAfWM5l3uwjGd431j8YEMikZ+K/2nXVuBqJKyZ0f+2h4n4JY5AyNiZmnY9qQr2RU3v9DxDmHMNg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-plugin-react/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rolldown": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", + "integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.129.0", + "@rolldown/pluginutils": "1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0", + "@rolldown/binding-darwin-arm64": "1.0.0", + "@rolldown/binding-darwin-x64": "1.0.0", + "@rolldown/binding-freebsd-x64": "1.0.0", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", + "@rolldown/binding-linux-arm64-gnu": "1.0.0", + "@rolldown/binding-linux-arm64-musl": "1.0.0", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0", + "@rolldown/binding-linux-s390x-gnu": "1.0.0", + "@rolldown/binding-linux-x64-gnu": "1.0.0", + "@rolldown/binding-linux-x64-musl": "1.0.0", + "@rolldown/binding-openharmony-arm64": "1.0.0", + "@rolldown/binding-wasm32-wasi": "1.0.0", + "@rolldown/binding-win32-arm64-msvc": "1.0.0", + "@rolldown/binding-win32-x64-msvc": "1.0.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", + "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", + "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz", + "integrity": "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.0", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/appointment-booking/package.json b/appointment-booking/package.json new file mode 100644 index 000000000..a10abc8ac --- /dev/null +++ b/appointment-booking/package.json @@ -0,0 +1,39 @@ +{ + "name": "appointment-booking", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "format": "prettier --write src", + "format:check": "prettier --check src", + "type-check": "tsc --noEmit", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.6", + "react-dom": "^19.2.6" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@typescript-eslint/eslint-plugin": "^8.59.3", + "@typescript-eslint/parser": "^8.59.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.5", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "prettier": "^3.8.3", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } +} diff --git a/appointment-booking/public/favicon.svg b/appointment-booking/public/favicon.svg new file mode 100644 index 000000000..6893eb132 --- /dev/null +++ b/appointment-booking/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/appointment-booking/public/icons.svg b/appointment-booking/public/icons.svg new file mode 100644 index 000000000..e9522193d --- /dev/null +++ b/appointment-booking/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appointment-booking/src/App.css b/appointment-booking/src/App.css new file mode 100644 index 000000000..f90339d8f --- /dev/null +++ b/appointment-booking/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/appointment-booking/src/App.tsx b/appointment-booking/src/App.tsx new file mode 100644 index 000000000..f3d615c1a --- /dev/null +++ b/appointment-booking/src/App.tsx @@ -0,0 +1,102 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from './assets/vite.svg' +import heroImg from './assets/hero.png' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> +
+
+ + React logo + Vite logo +
+
+

Get started

+

+ Edit src/App.tsx and save to test HMR +

+
+ +
+ +
+ +
+
+ +

Documentation

+

Your questions, answered

+ +
+
+ +

Connect with us

+

Join the Vite community

+ +
+
+ +
+
+ + ) +} + +export default App diff --git a/appointment-booking/src/assets/hero.png b/appointment-booking/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..02251f4b956c55af2d76fd0788124d7eee2b45eb GIT binary patch literal 13057 zcmV+cGycqpP)V|)f$;Qooc7=_G zlYe)HToTQIc!$)^+J1M1y0*T%w!p~7%ux`!eRhO?c80XDxKQ*R^lUUMnA>6NT^?feoZ8xxvP32D&s-9ow zqjcM}eesrC)NeDmsf)*P7wJ|K!&xP%Zy4iI8lF)Tv2!reW)tCzg_1=PmOwd1SQfxa z8;58t!=z~Ba7CYlNWVG>he8aRPY|+-JmozNhn!#9i#77Aa_Edt$ijyCWL#=~I>~2X zZNrQ8I0=D+NWD4pq=7~(i zhfThMNw|G>g^y9pGzxX7ZSApl@tIxFcs{p#MX{Ax&XZT+cR#U+OWc@S)pkIuI}dzu zH?^Q=<(y&Vq-oxSLfc0Zmq81bjZWf}RnssBaD6}2g-XJHLcN_|*IOu>m|x$nbm(?E zyNy!Zp=RroS;?Vg*kmoJYBi!n5{_^@rA!)=t#a^;N$8GL!*DsQb}`yvEuX!G@||An znOfUZAevPrkV_qjl|<~3QRZzG&h@C9Y5z zqpNH4xqbF_InIPh)kX}Vn^5kyed|mOuq+2>M;v~KO37a#yrEn3XDqtOl=rc6_KZ!; zreo)DFVB4|>1Zd(bvMI%8uM;3!)YMYu&cG?(PE!B~y@3yKBMt|R zAf=I16tFwPsl)!jDqvYkLHaAQ+f@W1m6F5aZvwhm4JL z{_l)@b;)mDSzle2gyFP5-r1x-5X{G}ot%VyWP@vEW80!Q=f%RTfpg>B*TA^pyWYUQ z<=xPtz}WcZ!;rFl4m1D&FFHv?K~#9!?A%+fn=lXt;9!Fc#kQ;zk~gZFsH z8e5iu@c_pzX&qb8&Dum*oXwB+fm6l6gFfC|o*wgEiy6tw~&co z9Vd_4)P%wP-KwQW7|lN-znGK#?N+j24U=$982myIBM+vsiKsc*@4-rwJxuAaHKna6 zT3wi!C~a4ZKH03qU}_1bKyx0&$CaK7_%Z+Kl$)fF5^op zZApQF2TvDav!s|krTjw-8US6ep z%!VmX4luub+fseQz_D9ATJQ?iQQwD}TZz{-yo#l12a%+7bT@E(X-hyaVS-5vuXc#^ zx^w;L21;NphGVoj*{s3f4dme0y2LC=G1-7THd`#z?;tuC{^9k(dM{Rf2GOxg7Jzho z7nSZHl7?M9kdalX`)YgoKEfiae5+;$(OGeN1eqxrv!ZCVKyH>xiyNqfe8xzY8*7)H zQls8KMp)F4D>ED;idMOU^^WhVF@q>ZSmeB0y~qC~|DB648hr%Sh|*T(4q|w2l?m2+ zvBVw3@7+Mz?^Yc#+se6KM;a<=(W-I>k)$-qL2V*t}VaW`;?P4)WqI%maIDq8!oUcSYAD`}wWjkSyAVsnF65#2zQ zZ>(K*TlS(E#4y$4Zq+e^_&}d)q20hCe3!LfLYP%nQpLJ~gM6a1hJlz3)aS<9C9me| zAcmJ#>tOwBy{HoP0Sm1&_(E+S@6 zgBIFUoei8zJmdpiq8q5=OY7t@`)JWxn_&GvKVr=Zdb_pEL_j|=?f;WK^U9Q0efd#K z9q7SfJTl4pmA$jsZ5oK8@O9#!I3Cv-kL)<8SalSsp#dcpvJ}Nz#G6FC0%9|7Fi#8; zGDJXtj!&GljT3*HE@0EE>G8Se&d)*nkqe}-?`3vPl&UqK?xG z!3XJ4M-x`EuQjhBbu?ik-)rmIt=DF_N?TVMP)8Gjn)TZ2V%H|zENbeix}kOxd@0}Q z>)HuH6Ean!uS#~4g2Ne2WsMGel|h%j9*W_quQheG^JqmKhc*RYzp0wKlGjBq2VzY_ zgOv8WC1+%W=W)k)Yp_`8kfE=uiiwOZTXi8Uj9YGr$f@yJcJ;#&-Nq~sJ7anE(@;QN z=~br%7%7`isKStX|7!1?L(apl^QvPKlrHV4S+6tNVQ*R1iGdC~WMNE1$a+=rpQmcB z>wxiLIBvOnm;u*;9Y!kJdy(T4lk|8>JAm(&wEsFIF1$_*{>2ZNd$V6DS=SfrGxAv0 zzKe377JI`&o9Ljr+VnS*EwehA{f&{cKZF(6*MG5!p5MvrFA3ll{fmRG*L@6^cb;o^ z3Wm8c?Sc6$`>~VEWw(c$Y?nRO;2Q$=ulpqPtM^=1IZx;@xK0PgO7rKQ^WHVLwtgUT z%|JF{^f(VH)wLKQ%dYiu2RmchBdxL0-M?wxxul_z*{h6ZZ`>-k(vizs((vW8Lt6Z6 zY;Dt?@JWyN`O`f;&d1Mb?e%9oyRK1ql?EE5XB2(W)|D1~Rx35$H6@6)$F?)7V|zEO zI}fu0-0}8W5=6sg$fPnZ~7=tTudl?Ecb@pxbo)vni%gP-?hL|%*?62C;x6?@E`VRnJv z?fTb;k4x;TS7Cu-z%J}uy}e-pwpLQ17Q@4DC+FCdAmNKklG$`I_pyw7E{fYmw~{Fj zi?6KcVy=Wrel)EB_DWO|0CKmI|13!gBV?X`Ozp7x>?6jr`>Qz=^4ea35!$*f}) zS$i+x_k+@P2q1RFUH^ZTTk7=n?cjfR>hTq3l3SY~#w+I8SSutXGyhw;Ws~=zMQ%Vc z>$On~47Ut?P*_!TOQ&PFmLAyJieB2X4_Fd_!WxI-AY`q1Lc-oK?+qcOTzlQ?@~x@OT}*9jTVNfl@3rGvZpWI=eKg>T zZb@6YWz)J=IhP7CF|c?G62vMEG%#U}?#86$0jR4sG~i(jRd#jmn`7b(O#?N;3a;1t zhXLssmUwGhp79luw#(*V8WL0|8+E z6=YZ_O@er~$LrD_PYGc(kJgB=;yw#+Z3X6LDUZ(NcwN=B-hjdiHm!JFar%m{(5bEW z@@_VEtG$5;`EJZ|OkJ@l&G9n((w@uNFwmU%bG|s#TbcJJos!{e+bjCjrCq_}LcN!UFgKtgg7siV*7# z!}1whTRRi*-avJPu->C}Z8EiuK$#886+H_#_!btv+rsiBbv2jAJvJ+O0{#}y(%L3H zfjU-kq_-L@2XrL*ae{{qYJkD{@dw%*bkh2P&YS-0!Xt!PRz7KHV0+~j(t9W8lAVWR zt@B*DgURgEz4>WuN>o?_iKcw$?k{||Pg7{Q2o4|VmJ)mg?{VQJA<}zEr^YAAS zgGm5RT4T3p)U;yz-tfBO^kw8?IoG!IVmc+Z3m#}AOQ?5MRa>)OcU!$N^_+yK6ayn? zK>~WK0!#ysuj^oNLakm)Zvu+J)OSubX^kv!c*xgdIvs;kln!rgG4*uZ;w0mQQO4XD zO9P{GNdv!=cQ(CAL{S(%KtuV^zC&Q{%g)PoXnp^gn^>c*`E>$hLYg2HjnbVGtWLa{7zHdG1jT@B{|Dm16 z7K2(jsfG+m*Zxof)iXxu+!H5Mo-0$pkyV3VV4B@Qms46M zuBxGRV@HxU7Wwx-6CB zaU*HO<_qn$5GH>&@?nRy1{z zkik!sLfWQ)r#75)vVwCBU*r_)Q6mp?!j85{#Xqse)ApRdE$V0%I0*~e(_{)5H)`Mk z#rExC>yjhZxuL@|+#v4#<Axw$+VpV zuT;!2Vww$je$DpAW`$FX_Ab|Ip%$;&T$-lW8jS~B$>G}rd>eQG+$h9lQx4Mx0w={m zx9?T6VU`>sR}XClkAhHEShOUe8awiq zmizhL+}5UKs3}6~It7vBTig9dfQ2Q8coo+Miiaw7n~>4ybv2Ptt0^^=VqX(t*Yya9 zr`FxxFX8(v*H=+uJ#JJWIB2A(==HDYx~^zZ2nu?2`}|Wsa*f3h3ixc+U|FDtAG$Y! z*lc_7se5Oso-Cgqe0){{!8H4g$3<8!R<6JOurD;((({c$1(pwb>(#TT!sge@4>r2@ zVL7>U`0`nsWAYErezk4(Z!gMI2?UTo{J3Ajo(u4)KYIRd>BRcG4BoS3G0EXyEp@tw z%P7__?A^a>Q&AKL@ayDO9D*Qkc!NHnO9l}kpp_6hXbMppYL(X1L?njdFT|-h2<_$; zAtDZ!1Rf%|yb!qbWKd}%0b`LzBeyNy43|QO(&h2mxQLUL)|0%agVOW)6TV!&Ip^Ls z`PG2cygM8)IecQx=Fc+nqYRo4hS^^-nM_&-y8?EJXUczP=DIw(GkTJdpEdh<_STs{ z|A)4n1GKdE=Wu!!nYoZHcUQ4S&R;oDOKX2lrkdF(mK>hz<$Pp>igjOcvoRIjlN=W8 zu8Gx5(roqn8$>gEE5vy{GiGeW8Tq{vnf3hS-V=$tZkQuftUVuU8o6k&dn=Yg3)6MOIH>nlK^-2+C6BZITr~1@So?NvG#TwL)|~=1YXGMTLpS<)ziK_CSOabe z=cB#5)yz|@0i9dSo?*CX)}UP=s6)B+F@~Em(u@Q(I9J9i_V{LmMu8BfXYMh~*oPP+ z!3~xTv|(>|=n6ZOtT~C@V!z!w%18*8T2t6}U2S##rC)mekBql&VsBX;$~ByGE$oA9 z`0Wzq8p?R{4)$l*on;!cLa}Dh^Xe?owiQZt9nH1fxxh$pN9K%CtOw?u3>85L7rr!d zXs)l{TZ{xXP&U8exz?9cv~dNNibOmt*K4I$?RxqIBZ0(?Mg-9FS{*9Bc49Qc1`=sIF-rye`aNT1G@4NwXcnyc@+bw_mTsR>5< zF<2;X0QesG_pw|TonqVBhRtfqI>ty(SIu&VOXd0CrLlfp+;WH7HYjhqnu^oAY!9cB z=B6#R?Rfz9BP`dJ=@v_?70s3HxQPk+{6Y+lM85f2NF^00*^OcM0~?JOZfR9ZPYF+# zYSs}(_BUYV8{n@2a1hD^SV41bwmi2uztR;PeBgF1F-`9>`zoNss-@3LaF2sjl~>OaaVmp7PNp+UT`6@}gR%uzqHDVeEZ14{Yt?n%JeQm+t(1_u zSc}oj^{b;+rlS|ME%+LjzSI&xu0Bblxo$MJ-J$kJ?Qu_XUXh}*@*-x@ny|}wVM%Lg z3tNB`yvr*}N?ClGL;H2cglcvErIccU3(eP7>@~4nOIcI~-`P8tSQnx=jI&{9)!1}l z;gQ%_h>ZlPSV@o@Azq1R$C6ja5!^ZGh;YRhhxs58qJWo9@Bceac&yy(pET1hnn`~7@}2L0&dfPKYs$ih7m2}R!25!(hxqA(!UIw; zK4+~Jowy3=RNC6nE=ncU{LH5?*9@W24lacJlvCZXB$CYtE@>c+~H zkV=(5I&gb{xn2!~f&fs2NQgAL6`p|kyt6kpWk}iVlqIp(H;ig`{_U9yxs1jzu^ETM z7~)Rg8C-NueqTYP&U8l{DY=Y47cR zOR@U%$KQV{mkRF|4)z9Y^t3K`@p>duY&QLUFeh6VoV`a`$U@)(z!-N*5Cj<11$EZW&hJLX83TO{lJYP74rlDZQPkm@t<=U^I)x@|UnHHkdQlh?!ltZwl92rE;;^ zZuIappj4dhld1}kttYYV-j|KF1Kus zWBnzttD^00%LFK(wrwNragFub6xiV8QE2rm<`&fcR4SLFcdtLxVuN!Aal-g6dE4%k zARZ}|xeo;K{0yf7@9aua%2j5o)CPcIOc6uLHFJOcgtB5owlcNAwyAHc0QB0Dts?c@ zUemG~j_E&W7R%+x-IO4FJl8e&*2Blmp1S#RA|)geVrxvP)NHdYuxi~g&Etn?QdNK8ZDKZ?QFLU?zh30G|t9G>a_X4zk}Ygw<^$7K!GIn(Io$>(d4ODJQ2XSd%jpK zm7>ptl$a3GyB}5-%p4>Q*p#VL^B{yQMuFCM^#l#+N!Ne z5_PrJWB=@Iy+t)H`g1lX`{bm($KE5I?0c(JEYm#t{F}j!xtsbob0{xu@0TB_*>G7w0ICn zr#VoBktqHZ~XxhiKD*lcG|b;H*|Ny3P^8ceV`sfBRfrhwZ!T+MFZ!F1Bt{q$8d9i6o?~ zODj^POr}&ivSa^R^YFIq7o0giLBKCycH_aU`F6)O6JX%nPTwh~Q`eq6*0iE#Srj2^ z*_hN3%*b83zfafy60@Cp3{J({RlSaEn&E?mrxRNC9GQ7#+f=s! z0KBf-9Ny_v2VbE%aB|Di)5kNJ^t&C`4D(>t7zYUWUFtbxt+Oq=!@O7BU)}>d*R72o zFF)3jQD_lLe4is&xzyJYC1-c{8TX$RU>&>P$%)ufpez0XSAukmh!xcekg`s$c<>-q zI#zn^JU0zzF}V60)o$_gY}PQH>b2M9&8fRZa#OauglPb zeQ@pMm&=!vNgos4CluQjLMV!pfkmxK+35bi^k&=k>9h02?l+u+m0agG;(h2|Jslc-llvtEwn~*w3bx7qnvZACG<8}AGeaDVvcHbKd2>3G^ zSFPULUn-?Pmo^-_`mLZr??uNH`2=I&yajlrF{DtUxMy#Nu}z=3y7qbUA;5`)hibMR zhXL@@uKyV0-2&A@t@!xyrBnMJl&^o@Gx$&5_q6?D=ji5grd-~=?dlg;ur(_V0wjh! zA=JV^C1m+DDkOsgr<%O9ZQFg!0}pD(#PSz4Dr_EyS5$`)VIAv);4n-SFP~YtC7sH= z7&*MfpH;gd*FHbkmD#)hVxb6xjc9~`t?_{=JS+@ip_cTicXxG<=7m9& zPX+Z8IC*GSAXuGCrZDHgR$r%jyk-fctis2Kx4HvZ|B~8uC@o)m^>Hy-O!&TKA?$&n zkP2Xc54w~!=z2?^NafyL*L0V9cbYrugHBBUj`xVyZmGFR&kvk#>1J*Z~i zNTz}?IAdJ$gkqd2!Gw(%LzE!O5s4C7q4%T~e_P{+z=DNDKrG**p=U`d5yg^vp`;Zn zsU=8gd0a9s4s0FPJePWR9eH5=+O^Kks&kC-iblNqTh2&Pw*^(4384f+D8N|fewZu_ zg2ejQ)ov;ztz;NQl7yj;A`(!H!XQu_$sqY9h_IrH*}_%1{L&_YLDvO?%R5Z-t+ClW z_qERbL?HKUZ!nt+!E9S`uoh^5A|DaIHe*_gf1`E_Vq+}{&T@t$EGhMnRjJ4z2w_W8 zp+qjs7as22^&S3wY1?+}^j-I=RcCE>#|39)g(lU7v_8;?=qK(9D8-*pPdiy)P3lIblG`+?%ea| zYoD3dopYt!tKgFicfNmNi(EWE=E4hC6(r|PYtanqJlmt57YOVrr2^tfrG(eG9C##X zu&1t@%L$RIvpj!wUA z8i>Pqot#_+Cnp6L2XPcZy1ar|9MnY+7eNvK1E)@Tr#2KsXq1*>)uUCozT7L##ok?o zhA6ofP4E|b*9tAfG?uf$#}>TIR&1A!yslP8}i7w-EzW(x#9VEvx18k%Tn=-$VV zkOtUr0b2!w3t>h?#8AZl^Az*(6KCGlD;4j~yx};`#2gN1_gv=%7KVzecIRakN{f*4 zeaI>yH;-o4OGhvGTU)(quWI)-q?V*(sVesSMv|wMUQ3hLEt=lBB$KZ9TyHr>)f7o%) zPYeU<3P)*P10*7vE)nA5#{c=6-E-_>r_u4e3i!I2+UksELwDqwMeBZ9FSP$;^Ajro z_@M#_Ss$?ejoB@!wN|kbGKs(0zLo%0QpQXW#t;oC$B0MZYZ&Ej?8~fNhcCVvPo3vo zFn0WWZaPliF^8_}yzb`*f@yg0uWv6HgNI)xa=pO%Ck(C<=-60l#uD3(wXP~c7!NoX z0&^6=N`zcc90F#qt@=Rn@r!3(*1v(Tl{B!m?Mc7yIA+nEHpY{YWr$=)F7rhR1P}(v zt{YhY#;jsW6G>#xhP*B`OCk|Pf+NN;ju1rxa*HAgoGq*rvqw&xe~;t1JA31$s?GBb z*g7&@cbKo4n<`>)!UlIAgR6q&))B0KYU8r66GbFj?8Guw4E%&}Qi_lT003LtoIZei zwD~=XZmeo+yZ2Pq3KYCF-R&11^p= z@H%s+=G`}wrbJ{()Mh71#2SP3Zy3m>l1n?0N-N1Q;z6?oSxr-G(H5m4EO>~&;}VKi zfY}3w+9z>vp#d)hVuu`)vG_aaH%3b=WKMnSu&c31;<3O;bz2iD=w+o4#oBb36 z5ZCF*Gu?zjZIR0S>_%pHY2$k8D^n7Sz_K8tCDeXM+dO<#LSg%h6`~dnVG1N@T7v&e z%wEd1!k{^zfz_1BTW{!$!B%g)J^2b87!9Y>>100X1SgT7s0z$o>^lAA=Gp_cC1(h=*5Tmf8z&LGJJ>$|K^~s`z9*OWz5MFUr?>Bi?_PGBB)#psD5?>n+q{o_ zz7~ez&;t#h8l$jwGPCC&xq2YetXYQT+0F3j(`xmNGf8dj#an|p#I*pvI*kwW4iuB> z+q3_7xB8y;pLzHG-S%+UHQA zvqp;$kmGJY>lLsN4C~&TcvAS1SErTcwcw0r@wngk zShAUA1M9b#g}^pL-zH7Q#z^&j#r9F8BTVfkR&qF<=e35goTu7c|GN)0mokj4m0%~0 zXJ8j4Hc_l;HJ&uU*Iw`8d_EscJ``s0tk9mkKo^&#TYXm-EoAzTQObxa@^u~g2t#T) zJz|rE!I_?i4dCJC=B8(_pZ{YR>|V?0iCcnU;E@$239^x?SYCfNaMHN;CtHIS_zHN9 zTkQc1v@O35okiFtq5_u+5FkY55ap@pi)O?}x0D1c*qB0KpYR}>Ul+B0Vmr}Z@+%mJ|As}sis_=ROPbov@*2thpE&?!V#Qgu$snYvCZ zrkhmkMU+fSf-s8(L37fPr&M*jRs{{THb!aXQu|P9l_-vJhHvLzMGH zE?1U0H_+PmNABp9`|KzkGfrrZ%XvdGo6*<{d5m9~L7 z_^`M;X6xDo=m6LY6RfvJEvsTK1!u8d2HPx|$S}p;sRy!I zWL55Yxu~_B`OP@~(q6&W3#)~I&+MGL%GWR$#udC151^wsswhqlii;rP9jJpiI7o&Z zAb})=HY7?4HA|re3ns`%$)FuvKCFWjhb~?IE)F6dF2K5}poj-NK6Gf;hw$t3=1txY zoxQxZWrQU6K!%|~!m?~Bnw-6Rr!F3BZ{u5!LqnZTDON}Coj9^@&le)V!NYrVwS~B% zEL+>Sr@}qGwGvu|HrOo|gSt__ezN^&%~{*)a=rf7y1HujUcr`zZB<4#l@T#eN)si} z)lZA<{=tKx8E%c9>A(##6}_p+~EZpKsl5a4pj`E*;_-6`ysiv zffA!7=MT1vCz}-m4~tjVey1b2KSR4OEtLd-(_DdUqYZ74LaDkhH?KFh?%WAOP2WbX zp@zT+Dx|5_f%JQiAGvVw!oh+g3e50u!aPfMxdC=E)XB{F5IcEZhePIM- zph6Y`$Oy?JBL<8Ex(SqEhLeQ@XcrdA>a?rx+_~HLA;l14)WmmpH}_w?Pg#HBZs0eS zwypwAW?M-x+3AU-(GGWSJ=ngxUEcEZ5OsX(Qlt!MQ zn^(`S{GHkAv(8@D`EAfSYig%Cxv?z!{=w^F#y)5_d7FuKZH7qlR-#5B0bt806%D0I zT7VdVP_?q*%Rq8UR;JkD4i^RXowt+E%#V2U>TfDqzZSDZ+dR!a#T3I>-z_$q9@k|m zy5~A*m~&JWP@E7a=pc}4kVHTc4h&R;Li7d@f`|hKMLkbb^uhOakNr3&FLjlm~i5NBM< zFaYI{;cpiHCNRdE0dg*>qIm(_t?#$h=(SCw?h3rJV2*ER8{O4^3#=dO)KwklZkoqU zS8i5c%YL*y*4;FY#D=XmkQnYj%LH)?02~gSJH`Qp1XY64g>%c_K$xseI&|e)7vRoL zAqRba$G@%fSGA7X7hQk%_3NVOYVS+$leU_!&6*5uN)8#5ZBz_6ASCA;azYS-Rt@ki zg2NWz(=;t}SC(~Ibl63$5C8FPmhXqb^)5#jaJ~I{Ex3xZ!+2h8$}}h_g@Be>HZ;72 z6#y#>AY3^skuVKF#0WxFBQ()5d5_nWb?c6c>EeMM|Mh+*&wEpPyxHCq{R-Gdr-`hN zF=1sxl&mBoK+#qRLl9#CEN|Fg8>nbmsTg3a1;#M9enQ$RgWk}kp#-5wh=EF&1tl%mJln2V^8o%Qv(*=zEuO7y z=m*8?xpUn-*@h5Cl_3BK3joiGkyaScK+>|MWdMRWm@RT!Q1piAlv5hL@B6>3&GI8) zP!xBc6}ZNIpJLL%2a8Y!+(<=f%WX>_uWVxlga9!D*oYt$l0cxRDMvqfU;Kq_mLK5k z)dvqYcgLa_Lz?3HyeF)@$%$&6lI?r4I>6W#M*<)vq{?&Oqrx``d`mhpVPr> z#q078F6gw_X<=?KR>8%^t%@wbITvNMu!hKiTSkCTJkw>1!e*Y{%31#_yMf=LW7{RJ zYoC^w$6%3cBtVG5)x#{Hg6IVTh9XEcM{gQwXk!R^y95^f-hZ`d{aVa+xW1EO4wDV4 zB?JgD7*?qkvc|$nIykTvNl2x0j3Q!MXoLL^)~}d7jcYf(H8D~c+?$pKL(px>Z3`eb z04RzS6_AgFT6Pn#iZAg$Sl_j8#;6ShF%&(Fag#E2asU@@LaN;=b=Wf7sgPKhfzhBM zC@eFL8^MrnA*9&Khe*Ab@CC9*uyJGXyi(;y2>lQLJZt;ShtJi?3Yf_t`F+$hY!+Q2Ndsx=U+bjTiAy7djLji>7k%k`$9&--f<*BNA3Hy&ZrHH|4 zG5H&9cB?O#zI1_OOf0Ce%mDfQxdtp3vU%(iY6yji3iISS61XLv#z|!zI_sZqza@B+ zyu9st5-h+`H7QUKx9}3w@oU@EO}&cEzG?fu!!bLO->%zkcg;i9^j`S~=WKMnDi1f= P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/appointment-booking/src/assets/react.svg b/appointment-booking/src/assets/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/appointment-booking/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/appointment-booking/src/assets/vite.svg b/appointment-booking/src/assets/vite.svg new file mode 100644 index 000000000..5101b674d --- /dev/null +++ b/appointment-booking/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/appointment-booking/src/index.css b/appointment-booking/src/index.css new file mode 100644 index 000000000..5fb331302 --- /dev/null +++ b/appointment-booking/src/index.css @@ -0,0 +1,111 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +#root { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} diff --git a/appointment-booking/src/main.tsx b/appointment-booking/src/main.tsx new file mode 100644 index 000000000..bef5202a3 --- /dev/null +++ b/appointment-booking/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/appointment-booking/tsconfig.app.json b/appointment-booking/tsconfig.app.json new file mode 100644 index 000000000..f59ecdbb0 --- /dev/null +++ b/appointment-booking/tsconfig.app.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023", "DOM"], + "module": "esnext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Strict mode */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + + /* Path aliases */ + "ignoreDeprecations": "6.0", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/appointment-booking/tsconfig.json b/appointment-booking/tsconfig.json new file mode 100644 index 000000000..1ffef600d --- /dev/null +++ b/appointment-booking/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/appointment-booking/tsconfig.node.json b/appointment-booking/tsconfig.node.json new file mode 100644 index 000000000..d3c52ea64 --- /dev/null +++ b/appointment-booking/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/appointment-booking/vite.config.ts b/appointment-booking/vite.config.ts new file mode 100644 index 000000000..ace547ded --- /dev/null +++ b/appointment-booking/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import path from 'path' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +}) From 66a38a4c7a7ad1c562aa9ad8a62bfe35a5dae487 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 14:57:51 -0700 Subject: [PATCH 48/81] dev-44: add license-check script enforcing MIT/Apache-2.0 for production dependencies --- appointment-booking/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appointment-booking/package.json b/appointment-booking/package.json index a10abc8ac..37e64d034 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -10,7 +10,8 @@ "format": "prettier --write src", "format:check": "prettier --check src", "type-check": "tsc --noEmit", - "preview": "vite preview" + "preview": "vite preview", + "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause\" --excludePackages \"appointment-booking@0.0.0\"" }, "dependencies": { "react": "^19.2.6", @@ -31,6 +32,7 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "license-checker": "^25.0.1", "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", From ad730883e31312316bdf20473f9998b983a181b7 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:01:55 -0700 Subject: [PATCH 49/81] =?UTF-8?q?dev-44:=20strip=20all=20Vite=20demo=20cod?= =?UTF-8?q?e=20and=20assets=20=E2=80=94=20minimal=20shell=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appointment-booking/public/favicon.svg | 1 - appointment-booking/public/icons.svg | 24 --- appointment-booking/src/App.css | 184 ----------------------- appointment-booking/src/App.tsx | 99 +----------- appointment-booking/src/assets/hero.png | Bin 13057 -> 0 bytes appointment-booking/src/assets/react.svg | 1 - appointment-booking/src/assets/vite.svg | 1 - appointment-booking/src/index.css | 110 -------------- appointment-booking/src/main.tsx | 1 - 9 files changed, 1 insertion(+), 420 deletions(-) delete mode 100644 appointment-booking/public/favicon.svg delete mode 100644 appointment-booking/public/icons.svg delete mode 100644 appointment-booking/src/App.css delete mode 100644 appointment-booking/src/assets/hero.png delete mode 100644 appointment-booking/src/assets/react.svg delete mode 100644 appointment-booking/src/assets/vite.svg diff --git a/appointment-booking/public/favicon.svg b/appointment-booking/public/favicon.svg deleted file mode 100644 index 6893eb132..000000000 --- a/appointment-booking/public/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/appointment-booking/public/icons.svg b/appointment-booking/public/icons.svg deleted file mode 100644 index e9522193d..000000000 --- a/appointment-booking/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/appointment-booking/src/App.css b/appointment-booking/src/App.css deleted file mode 100644 index f90339d8f..000000000 --- a/appointment-booking/src/App.css +++ /dev/null @@ -1,184 +0,0 @@ -.counter { - font-size: 16px; - padding: 5px 10px; - border-radius: 5px; - color: var(--accent); - background: var(--accent-bg); - border: 2px solid transparent; - transition: border-color 0.3s; - margin-bottom: 24px; - - &:hover { - border-color: var(--accent-border); - } - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } -} - -.hero { - position: relative; - - .base, - .framework, - .vite { - inset-inline: 0; - margin: 0 auto; - } - - .base { - width: 170px; - position: relative; - z-index: 0; - } - - .framework, - .vite { - position: absolute; - } - - .framework { - z-index: 1; - top: 34px; - height: 28px; - transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) - scale(1.4); - } - - .vite { - z-index: 0; - top: 107px; - height: 26px; - width: auto; - transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) - scale(0.8); - } -} - -#center { - display: flex; - flex-direction: column; - gap: 25px; - place-content: center; - place-items: center; - flex-grow: 1; - - @media (max-width: 1024px) { - padding: 32px 20px 24px; - gap: 18px; - } -} - -#next-steps { - display: flex; - border-top: 1px solid var(--border); - text-align: left; - - & > div { - flex: 1 1 0; - padding: 32px; - @media (max-width: 1024px) { - padding: 24px 20px; - } - } - - .icon { - margin-bottom: 16px; - width: 22px; - height: 22px; - } - - @media (max-width: 1024px) { - flex-direction: column; - text-align: center; - } -} - -#docs { - border-right: 1px solid var(--border); - - @media (max-width: 1024px) { - border-right: none; - border-bottom: 1px solid var(--border); - } -} - -#next-steps ul { - list-style: none; - padding: 0; - display: flex; - gap: 8px; - margin: 32px 0 0; - - .logo { - height: 18px; - } - - a { - color: var(--text-h); - font-size: 16px; - border-radius: 6px; - background: var(--social-bg); - display: flex; - padding: 6px 12px; - align-items: center; - gap: 8px; - text-decoration: none; - transition: box-shadow 0.3s; - - &:hover { - box-shadow: var(--shadow); - } - .button-icon { - height: 18px; - width: 18px; - } - } - - @media (max-width: 1024px) { - margin-top: 20px; - flex-wrap: wrap; - justify-content: center; - - li { - flex: 1 1 calc(50% - 8px); - } - - a { - width: 100%; - justify-content: center; - box-sizing: border-box; - } - } -} - -#spacer { - height: 88px; - border-top: 1px solid var(--border); - @media (max-width: 1024px) { - height: 48px; - } -} - -.ticks { - position: relative; - width: 100%; - - &::before, - &::after { - content: ''; - position: absolute; - top: -4.5px; - border: 5px solid transparent; - } - - &::before { - left: 0; - border-left-color: var(--border); - } - &::after { - right: 0; - border-right-color: var(--border); - } -} diff --git a/appointment-booking/src/App.tsx b/appointment-booking/src/App.tsx index f3d615c1a..6bf84467c 100644 --- a/appointment-booking/src/App.tsx +++ b/appointment-booking/src/App.tsx @@ -1,102 +1,5 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import './App.css' - function App() { - const [count, setCount] = useState(0) - - return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) + return
} export default App diff --git a/appointment-booking/src/assets/hero.png b/appointment-booking/src/assets/hero.png deleted file mode 100644 index 02251f4b956c55af2d76fd0788124d7eee2b45eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13057 zcmV+cGycqpP)V|)f$;Qooc7=_G zlYe)HToTQIc!$)^+J1M1y0*T%w!p~7%ux`!eRhO?c80XDxKQ*R^lUUMnA>6NT^?feoZ8xxvP32D&s-9ow zqjcM}eesrC)NeDmsf)*P7wJ|K!&xP%Zy4iI8lF)Tv2!reW)tCzg_1=PmOwd1SQfxa z8;58t!=z~Ba7CYlNWVG>he8aRPY|+-JmozNhn!#9i#77Aa_Edt$ijyCWL#=~I>~2X zZNrQ8I0=D+NWD4pq=7~(i zhfThMNw|G>g^y9pGzxX7ZSApl@tIxFcs{p#MX{Ax&XZT+cR#U+OWc@S)pkIuI}dzu zH?^Q=<(y&Vq-oxSLfc0Zmq81bjZWf}RnssBaD6}2g-XJHLcN_|*IOu>m|x$nbm(?E zyNy!Zp=RroS;?Vg*kmoJYBi!n5{_^@rA!)=t#a^;N$8GL!*DsQb}`yvEuX!G@||An znOfUZAevPrkV_qjl|<~3QRZzG&h@C9Y5z zqpNH4xqbF_InIPh)kX}Vn^5kyed|mOuq+2>M;v~KO37a#yrEn3XDqtOl=rc6_KZ!; zreo)DFVB4|>1Zd(bvMI%8uM;3!)YMYu&cG?(PE!B~y@3yKBMt|R zAf=I16tFwPsl)!jDqvYkLHaAQ+f@W1m6F5aZvwhm4JL z{_l)@b;)mDSzle2gyFP5-r1x-5X{G}ot%VyWP@vEW80!Q=f%RTfpg>B*TA^pyWYUQ z<=xPtz}WcZ!;rFl4m1D&FFHv?K~#9!?A%+fn=lXt;9!Fc#kQ;zk~gZFsH z8e5iu@c_pzX&qb8&Dum*oXwB+fm6l6gFfC|o*wgEiy6tw~&co z9Vd_4)P%wP-KwQW7|lN-znGK#?N+j24U=$982myIBM+vsiKsc*@4-rwJxuAaHKna6 zT3wi!C~a4ZKH03qU}_1bKyx0&$CaK7_%Z+Kl$)fF5^op zZApQF2TvDav!s|krTjw-8US6ep z%!VmX4luub+fseQz_D9ATJQ?iQQwD}TZz{-yo#l12a%+7bT@E(X-hyaVS-5vuXc#^ zx^w;L21;NphGVoj*{s3f4dme0y2LC=G1-7THd`#z?;tuC{^9k(dM{Rf2GOxg7Jzho z7nSZHl7?M9kdalX`)YgoKEfiae5+;$(OGeN1eqxrv!ZCVKyH>xiyNqfe8xzY8*7)H zQls8KMp)F4D>ED;idMOU^^WhVF@q>ZSmeB0y~qC~|DB648hr%Sh|*T(4q|w2l?m2+ zvBVw3@7+Mz?^Yc#+se6KM;a<=(W-I>k)$-qL2V*t}VaW`;?P4)WqI%maIDq8!oUcSYAD`}wWjkSyAVsnF65#2zQ zZ>(K*TlS(E#4y$4Zq+e^_&}d)q20hCe3!LfLYP%nQpLJ~gM6a1hJlz3)aS<9C9me| zAcmJ#>tOwBy{HoP0Sm1&_(E+S@6 zgBIFUoei8zJmdpiq8q5=OY7t@`)JWxn_&GvKVr=Zdb_pEL_j|=?f;WK^U9Q0efd#K z9q7SfJTl4pmA$jsZ5oK8@O9#!I3Cv-kL)<8SalSsp#dcpvJ}Nz#G6FC0%9|7Fi#8; zGDJXtj!&GljT3*HE@0EE>G8Se&d)*nkqe}-?`3vPl&UqK?xG z!3XJ4M-x`EuQjhBbu?ik-)rmIt=DF_N?TVMP)8Gjn)TZ2V%H|zENbeix}kOxd@0}Q z>)HuH6Ean!uS#~4g2Ne2WsMGel|h%j9*W_quQheG^JqmKhc*RYzp0wKlGjBq2VzY_ zgOv8WC1+%W=W)k)Yp_`8kfE=uiiwOZTXi8Uj9YGr$f@yJcJ;#&-Nq~sJ7anE(@;QN z=~br%7%7`isKStX|7!1?L(apl^QvPKlrHV4S+6tNVQ*R1iGdC~WMNE1$a+=rpQmcB z>wxiLIBvOnm;u*;9Y!kJdy(T4lk|8>JAm(&wEsFIF1$_*{>2ZNd$V6DS=SfrGxAv0 zzKe377JI`&o9Ljr+VnS*EwehA{f&{cKZF(6*MG5!p5MvrFA3ll{fmRG*L@6^cb;o^ z3Wm8c?Sc6$`>~VEWw(c$Y?nRO;2Q$=ulpqPtM^=1IZx;@xK0PgO7rKQ^WHVLwtgUT z%|JF{^f(VH)wLKQ%dYiu2RmchBdxL0-M?wxxul_z*{h6ZZ`>-k(vizs((vW8Lt6Z6 zY;Dt?@JWyN`O`f;&d1Mb?e%9oyRK1ql?EE5XB2(W)|D1~Rx35$H6@6)$F?)7V|zEO zI}fu0-0}8W5=6sg$fPnZ~7=tTudl?Ecb@pxbo)vni%gP-?hL|%*?62C;x6?@E`VRnJv z?fTb;k4x;TS7Cu-z%J}uy}e-pwpLQ17Q@4DC+FCdAmNKklG$`I_pyw7E{fYmw~{Fj zi?6KcVy=Wrel)EB_DWO|0CKmI|13!gBV?X`Ozp7x>?6jr`>Qz=^4ea35!$*f}) zS$i+x_k+@P2q1RFUH^ZTTk7=n?cjfR>hTq3l3SY~#w+I8SSutXGyhw;Ws~=zMQ%Vc z>$On~47Ut?P*_!TOQ&PFmLAyJieB2X4_Fd_!WxI-AY`q1Lc-oK?+qcOTzlQ?@~x@OT}*9jTVNfl@3rGvZpWI=eKg>T zZb@6YWz)J=IhP7CF|c?G62vMEG%#U}?#86$0jR4sG~i(jRd#jmn`7b(O#?N;3a;1t zhXLssmUwGhp79luw#(*V8WL0|8+E z6=YZ_O@er~$LrD_PYGc(kJgB=;yw#+Z3X6LDUZ(NcwN=B-hjdiHm!JFar%m{(5bEW z@@_VEtG$5;`EJZ|OkJ@l&G9n((w@uNFwmU%bG|s#TbcJJos!{e+bjCjrCq_}LcN!UFgKtgg7siV*7# z!}1whTRRi*-avJPu->C}Z8EiuK$#886+H_#_!btv+rsiBbv2jAJvJ+O0{#}y(%L3H zfjU-kq_-L@2XrL*ae{{qYJkD{@dw%*bkh2P&YS-0!Xt!PRz7KHV0+~j(t9W8lAVWR zt@B*DgURgEz4>WuN>o?_iKcw$?k{||Pg7{Q2o4|VmJ)mg?{VQJA<}zEr^YAAS zgGm5RT4T3p)U;yz-tfBO^kw8?IoG!IVmc+Z3m#}AOQ?5MRa>)OcU!$N^_+yK6ayn? zK>~WK0!#ysuj^oNLakm)Zvu+J)OSubX^kv!c*xgdIvs;kln!rgG4*uZ;w0mQQO4XD zO9P{GNdv!=cQ(CAL{S(%KtuV^zC&Q{%g)PoXnp^gn^>c*`E>$hLYg2HjnbVGtWLa{7zHdG1jT@B{|Dm16 z7K2(jsfG+m*Zxof)iXxu+!H5Mo-0$pkyV3VV4B@Qms46M zuBxGRV@HxU7Wwx-6CB zaU*HO<_qn$5GH>&@?nRy1{z zkik!sLfWQ)r#75)vVwCBU*r_)Q6mp?!j85{#Xqse)ApRdE$V0%I0*~e(_{)5H)`Mk z#rExC>yjhZxuL@|+#v4#<Axw$+VpV zuT;!2Vww$je$DpAW`$FX_Ab|Ip%$;&T$-lW8jS~B$>G}rd>eQG+$h9lQx4Mx0w={m zx9?T6VU`>sR}XClkAhHEShOUe8awiq zmizhL+}5UKs3}6~It7vBTig9dfQ2Q8coo+Miiaw7n~>4ybv2Ptt0^^=VqX(t*Yya9 zr`FxxFX8(v*H=+uJ#JJWIB2A(==HDYx~^zZ2nu?2`}|Wsa*f3h3ixc+U|FDtAG$Y! z*lc_7se5Oso-Cgqe0){{!8H4g$3<8!R<6JOurD;((({c$1(pwb>(#TT!sge@4>r2@ zVL7>U`0`nsWAYErezk4(Z!gMI2?UTo{J3Ajo(u4)KYIRd>BRcG4BoS3G0EXyEp@tw z%P7__?A^a>Q&AKL@ayDO9D*Qkc!NHnO9l}kpp_6hXbMppYL(X1L?njdFT|-h2<_$; zAtDZ!1Rf%|yb!qbWKd}%0b`LzBeyNy43|QO(&h2mxQLUL)|0%agVOW)6TV!&Ip^Ls z`PG2cygM8)IecQx=Fc+nqYRo4hS^^-nM_&-y8?EJXUczP=DIw(GkTJdpEdh<_STs{ z|A)4n1GKdE=Wu!!nYoZHcUQ4S&R;oDOKX2lrkdF(mK>hz<$Pp>igjOcvoRIjlN=W8 zu8Gx5(roqn8$>gEE5vy{GiGeW8Tq{vnf3hS-V=$tZkQuftUVuU8o6k&dn=Yg3)6MOIH>nlK^-2+C6BZITr~1@So?NvG#TwL)|~=1YXGMTLpS<)ziK_CSOabe z=cB#5)yz|@0i9dSo?*CX)}UP=s6)B+F@~Em(u@Q(I9J9i_V{LmMu8BfXYMh~*oPP+ z!3~xTv|(>|=n6ZOtT~C@V!z!w%18*8T2t6}U2S##rC)mekBql&VsBX;$~ByGE$oA9 z`0Wzq8p?R{4)$l*on;!cLa}Dh^Xe?owiQZt9nH1fxxh$pN9K%CtOw?u3>85L7rr!d zXs)l{TZ{xXP&U8exz?9cv~dNNibOmt*K4I$?RxqIBZ0(?Mg-9FS{*9Bc49Qc1`=sIF-rye`aNT1G@4NwXcnyc@+bw_mTsR>5< zF<2;X0QesG_pw|TonqVBhRtfqI>ty(SIu&VOXd0CrLlfp+;WH7HYjhqnu^oAY!9cB z=B6#R?Rfz9BP`dJ=@v_?70s3HxQPk+{6Y+lM85f2NF^00*^OcM0~?JOZfR9ZPYF+# zYSs}(_BUYV8{n@2a1hD^SV41bwmi2uztR;PeBgF1F-`9>`zoNss-@3LaF2sjl~>OaaVmp7PNp+UT`6@}gR%uzqHDVeEZ14{Yt?n%JeQm+t(1_u zSc}oj^{b;+rlS|ME%+LjzSI&xu0Bblxo$MJ-J$kJ?Qu_XUXh}*@*-x@ny|}wVM%Lg z3tNB`yvr*}N?ClGL;H2cglcvErIccU3(eP7>@~4nOIcI~-`P8tSQnx=jI&{9)!1}l z;gQ%_h>ZlPSV@o@Azq1R$C6ja5!^ZGh;YRhhxs58qJWo9@Bceac&yy(pET1hnn`~7@}2L0&dfPKYs$ih7m2}R!25!(hxqA(!UIw; zK4+~Jowy3=RNC6nE=ncU{LH5?*9@W24lacJlvCZXB$CYtE@>c+~H zkV=(5I&gb{xn2!~f&fs2NQgAL6`p|kyt6kpWk}iVlqIp(H;ig`{_U9yxs1jzu^ETM z7~)Rg8C-NueqTYP&U8l{DY=Y47cR zOR@U%$KQV{mkRF|4)z9Y^t3K`@p>duY&QLUFeh6VoV`a`$U@)(z!-N*5Cj<11$EZW&hJLX83TO{lJYP74rlDZQPkm@t<=U^I)x@|UnHHkdQlh?!ltZwl92rE;;^ zZuIappj4dhld1}kttYYV-j|KF1Kus zWBnzttD^00%LFK(wrwNragFub6xiV8QE2rm<`&fcR4SLFcdtLxVuN!Aal-g6dE4%k zARZ}|xeo;K{0yf7@9aua%2j5o)CPcIOc6uLHFJOcgtB5owlcNAwyAHc0QB0Dts?c@ zUemG~j_E&W7R%+x-IO4FJl8e&*2Blmp1S#RA|)geVrxvP)NHdYuxi~g&Etn?QdNK8ZDKZ?QFLU?zh30G|t9G>a_X4zk}Ygw<^$7K!GIn(Io$>(d4ODJQ2XSd%jpK zm7>ptl$a3GyB}5-%p4>Q*p#VL^B{yQMuFCM^#l#+N!Ne z5_PrJWB=@Iy+t)H`g1lX`{bm($KE5I?0c(JEYm#t{F}j!xtsbob0{xu@0TB_*>G7w0ICn zr#VoBktqHZ~XxhiKD*lcG|b;H*|Ny3P^8ceV`sfBRfrhwZ!T+MFZ!F1Bt{q$8d9i6o?~ zODj^POr}&ivSa^R^YFIq7o0giLBKCycH_aU`F6)O6JX%nPTwh~Q`eq6*0iE#Srj2^ z*_hN3%*b83zfafy60@Cp3{J({RlSaEn&E?mrxRNC9GQ7#+f=s! z0KBf-9Ny_v2VbE%aB|Di)5kNJ^t&C`4D(>t7zYUWUFtbxt+Oq=!@O7BU)}>d*R72o zFF)3jQD_lLe4is&xzyJYC1-c{8TX$RU>&>P$%)ufpez0XSAukmh!xcekg`s$c<>-q zI#zn^JU0zzF}V60)o$_gY}PQH>b2M9&8fRZa#OauglPb zeQ@pMm&=!vNgos4CluQjLMV!pfkmxK+35bi^k&=k>9h02?l+u+m0agG;(h2|Jslc-llvtEwn~*w3bx7qnvZACG<8}AGeaDVvcHbKd2>3G^ zSFPULUn-?Pmo^-_`mLZr??uNH`2=I&yajlrF{DtUxMy#Nu}z=3y7qbUA;5`)hibMR zhXL@@uKyV0-2&A@t@!xyrBnMJl&^o@Gx$&5_q6?D=ji5grd-~=?dlg;ur(_V0wjh! zA=JV^C1m+DDkOsgr<%O9ZQFg!0}pD(#PSz4Dr_EyS5$`)VIAv);4n-SFP~YtC7sH= z7&*MfpH;gd*FHbkmD#)hVxb6xjc9~`t?_{=JS+@ip_cTicXxG<=7m9& zPX+Z8IC*GSAXuGCrZDHgR$r%jyk-fctis2Kx4HvZ|B~8uC@o)m^>Hy-O!&TKA?$&n zkP2Xc54w~!=z2?^NafyL*L0V9cbYrugHBBUj`xVyZmGFR&kvk#>1J*Z~i zNTz}?IAdJ$gkqd2!Gw(%LzE!O5s4C7q4%T~e_P{+z=DNDKrG**p=U`d5yg^vp`;Zn zsU=8gd0a9s4s0FPJePWR9eH5=+O^Kks&kC-iblNqTh2&Pw*^(4384f+D8N|fewZu_ zg2ejQ)ov;ztz;NQl7yj;A`(!H!XQu_$sqY9h_IrH*}_%1{L&_YLDvO?%R5Z-t+ClW z_qERbL?HKUZ!nt+!E9S`uoh^5A|DaIHe*_gf1`E_Vq+}{&T@t$EGhMnRjJ4z2w_W8 zp+qjs7as22^&S3wY1?+}^j-I=RcCE>#|39)g(lU7v_8;?=qK(9D8-*pPdiy)P3lIblG`+?%ea| zYoD3dopYt!tKgFicfNmNi(EWE=E4hC6(r|PYtanqJlmt57YOVrr2^tfrG(eG9C##X zu&1t@%L$RIvpj!wUA z8i>Pqot#_+Cnp6L2XPcZy1ar|9MnY+7eNvK1E)@Tr#2KsXq1*>)uUCozT7L##ok?o zhA6ofP4E|b*9tAfG?uf$#}>TIR&1A!yslP8}i7w-EzW(x#9VEvx18k%Tn=-$VV zkOtUr0b2!w3t>h?#8AZl^Az*(6KCGlD;4j~yx};`#2gN1_gv=%7KVzecIRakN{f*4 zeaI>yH;-o4OGhvGTU)(quWI)-q?V*(sVesSMv|wMUQ3hLEt=lBB$KZ9TyHr>)f7o%) zPYeU<3P)*P10*7vE)nA5#{c=6-E-_>r_u4e3i!I2+UksELwDqwMeBZ9FSP$;^Ajro z_@M#_Ss$?ejoB@!wN|kbGKs(0zLo%0QpQXW#t;oC$B0MZYZ&Ej?8~fNhcCVvPo3vo zFn0WWZaPliF^8_}yzb`*f@yg0uWv6HgNI)xa=pO%Ck(C<=-60l#uD3(wXP~c7!NoX z0&^6=N`zcc90F#qt@=Rn@r!3(*1v(Tl{B!m?Mc7yIA+nEHpY{YWr$=)F7rhR1P}(v zt{YhY#;jsW6G>#xhP*B`OCk|Pf+NN;ju1rxa*HAgoGq*rvqw&xe~;t1JA31$s?GBb z*g7&@cbKo4n<`>)!UlIAgR6q&))B0KYU8r66GbFj?8Guw4E%&}Qi_lT003LtoIZei zwD~=XZmeo+yZ2Pq3KYCF-R&11^p= z@H%s+=G`}wrbJ{()Mh71#2SP3Zy3m>l1n?0N-N1Q;z6?oSxr-G(H5m4EO>~&;}VKi zfY}3w+9z>vp#d)hVuu`)vG_aaH%3b=WKMnSu&c31;<3O;bz2iD=w+o4#oBb36 z5ZCF*Gu?zjZIR0S>_%pHY2$k8D^n7Sz_K8tCDeXM+dO<#LSg%h6`~dnVG1N@T7v&e z%wEd1!k{^zfz_1BTW{!$!B%g)J^2b87!9Y>>100X1SgT7s0z$o>^lAA=Gp_cC1(h=*5Tmf8z&LGJJ>$|K^~s`z9*OWz5MFUr?>Bi?_PGBB)#psD5?>n+q{o_ zz7~ez&;t#h8l$jwGPCC&xq2YetXYQT+0F3j(`xmNGf8dj#an|p#I*pvI*kwW4iuB> z+q3_7xB8y;pLzHG-S%+UHQA zvqp;$kmGJY>lLsN4C~&TcvAS1SErTcwcw0r@wngk zShAUA1M9b#g}^pL-zH7Q#z^&j#r9F8BTVfkR&qF<=e35goTu7c|GN)0mokj4m0%~0 zXJ8j4Hc_l;HJ&uU*Iw`8d_EscJ``s0tk9mkKo^&#TYXm-EoAzTQObxa@^u~g2t#T) zJz|rE!I_?i4dCJC=B8(_pZ{YR>|V?0iCcnU;E@$239^x?SYCfNaMHN;CtHIS_zHN9 zTkQc1v@O35okiFtq5_u+5FkY55ap@pi)O?}x0D1c*qB0KpYR}>Ul+B0Vmr}Z@+%mJ|As}sis_=ROPbov@*2thpE&?!V#Qgu$snYvCZ zrkhmkMU+fSf-s8(L37fPr&M*jRs{{THb!aXQu|P9l_-vJhHvLzMGH zE?1U0H_+PmNABp9`|KzkGfrrZ%XvdGo6*<{d5m9~L7 z_^`M;X6xDo=m6LY6RfvJEvsTK1!u8d2HPx|$S}p;sRy!I zWL55Yxu~_B`OP@~(q6&W3#)~I&+MGL%GWR$#udC151^wsswhqlii;rP9jJpiI7o&Z zAb})=HY7?4HA|re3ns`%$)FuvKCFWjhb~?IE)F6dF2K5}poj-NK6Gf;hw$t3=1txY zoxQxZWrQU6K!%|~!m?~Bnw-6Rr!F3BZ{u5!LqnZTDON}Coj9^@&le)V!NYrVwS~B% zEL+>Sr@}qGwGvu|HrOo|gSt__ezN^&%~{*)a=rf7y1HujUcr`zZB<4#l@T#eN)si} z)lZA<{=tKx8E%c9>A(##6}_p+~EZpKsl5a4pj`E*;_-6`ysiv zffA!7=MT1vCz}-m4~tjVey1b2KSR4OEtLd-(_DdUqYZ74LaDkhH?KFh?%WAOP2WbX zp@zT+Dx|5_f%JQiAGvVw!oh+g3e50u!aPfMxdC=E)XB{F5IcEZhePIM- zph6Y`$Oy?JBL<8Ex(SqEhLeQ@XcrdA>a?rx+_~HLA;l14)WmmpH}_w?Pg#HBZs0eS zwypwAW?M-x+3AU-(GGWSJ=ngxUEcEZ5OsX(Qlt!MQ zn^(`S{GHkAv(8@D`EAfSYig%Cxv?z!{=w^F#y)5_d7FuKZH7qlR-#5B0bt806%D0I zT7VdVP_?q*%Rq8UR;JkD4i^RXowt+E%#V2U>TfDqzZSDZ+dR!a#T3I>-z_$q9@k|m zy5~A*m~&JWP@E7a=pc}4kVHTc4h&R;Li7d@f`|hKMLkbb^uhOakNr3&FLjlm~i5NBM< zFaYI{;cpiHCNRdE0dg*>qIm(_t?#$h=(SCw?h3rJV2*ER8{O4^3#=dO)KwklZkoqU zS8i5c%YL*y*4;FY#D=XmkQnYj%LH)?02~gSJH`Qp1XY64g>%c_K$xseI&|e)7vRoL zAqRba$G@%fSGA7X7hQk%_3NVOYVS+$leU_!&6*5uN)8#5ZBz_6ASCA;azYS-Rt@ki zg2NWz(=;t}SC(~Ibl63$5C8FPmhXqb^)5#jaJ~I{Ex3xZ!+2h8$}}h_g@Be>HZ;72 z6#y#>AY3^skuVKF#0WxFBQ()5d5_nWb?c6c>EeMM|Mh+*&wEpPyxHCq{R-Gdr-`hN zF=1sxl&mBoK+#qRLl9#CEN|Fg8>nbmsTg3a1;#M9enQ$RgWk}kp#-5wh=EF&1tl%mJln2V^8o%Qv(*=zEuO7y z=m*8?xpUn-*@h5Cl_3BK3joiGkyaScK+>|MWdMRWm@RT!Q1piAlv5hL@B6>3&GI8) zP!xBc6}ZNIpJLL%2a8Y!+(<=f%WX>_uWVxlga9!D*oYt$l0cxRDMvqfU;Kq_mLK5k z)dvqYcgLa_Lz?3HyeF)@$%$&6lI?r4I>6W#M*<)vq{?&Oqrx``d`mhpVPr> z#q078F6gw_X<=?KR>8%^t%@wbITvNMu!hKiTSkCTJkw>1!e*Y{%31#_yMf=LW7{RJ zYoC^w$6%3cBtVG5)x#{Hg6IVTh9XEcM{gQwXk!R^y95^f-hZ`d{aVa+xW1EO4wDV4 zB?JgD7*?qkvc|$nIykTvNl2x0j3Q!MXoLL^)~}d7jcYf(H8D~c+?$pKL(px>Z3`eb z04RzS6_AgFT6Pn#iZAg$Sl_j8#;6ShF%&(Fag#E2asU@@LaN;=b=Wf7sgPKhfzhBM zC@eFL8^MrnA*9&Khe*Ab@CC9*uyJGXyi(;y2>lQLJZt;ShtJi?3Yf_t`F+$hY!+Q2Ndsx=U+bjTiAy7djLji>7k%k`$9&--f<*BNA3Hy&ZrHH|4 zG5H&9cB?O#zI1_OOf0Ce%mDfQxdtp3vU%(iY6yji3iISS61XLv#z|!zI_sZqza@B+ zyu9st5-h+`H7QUKx9}3w@oU@EO}&cEzG?fu!!bLO->%zkcg;i9^j`S~=WKMnDi1f= P00000NkvXXu0mjft=yBf diff --git a/appointment-booking/src/assets/react.svg b/appointment-booking/src/assets/react.svg deleted file mode 100644 index 6c87de9bb..000000000 --- a/appointment-booking/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/appointment-booking/src/assets/vite.svg b/appointment-booking/src/assets/vite.svg deleted file mode 100644 index 5101b674d..000000000 --- a/appointment-booking/src/assets/vite.svg +++ /dev/null @@ -1 +0,0 @@ -Vite diff --git a/appointment-booking/src/index.css b/appointment-booking/src/index.css index 5fb331302..8b1378917 100644 --- a/appointment-booking/src/index.css +++ b/appointment-booking/src/index.css @@ -1,111 +1 @@ -:root { - --text: #6b6375; - --text-h: #08060d; - --bg: #fff; - --border: #e5e4e7; - --code-bg: #f4f3ec; - --accent: #aa3bff; - --accent-bg: rgba(170, 59, 255, 0.1); - --accent-border: rgba(170, 59, 255, 0.5); - --social-bg: rgba(244, 243, 236, 0.5); - --shadow: - rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; - --sans: system-ui, 'Segoe UI', Roboto, sans-serif; - --heading: system-ui, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, Consolas, monospace; - - font: 18px/145% var(--sans); - letter-spacing: 0.18px; - color-scheme: light dark; - color: var(--text); - background: var(--bg); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - @media (max-width: 1024px) { - font-size: 16px; - } -} - -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } - - #social .button-icon { - filter: invert(1) brightness(2); - } -} - -#root { - width: 1126px; - max-width: 100%; - margin: 0 auto; - text-align: center; - border-inline: 1px solid var(--border); - min-height: 100svh; - display: flex; - flex-direction: column; - box-sizing: border-box; -} - -body { - margin: 0; -} - -h1, -h2 { - font-family: var(--heading); - font-weight: 500; - color: var(--text-h); -} - -h1 { - font-size: 56px; - letter-spacing: -1.68px; - margin: 32px 0; - @media (max-width: 1024px) { - font-size: 36px; - margin: 20px 0; - } -} -h2 { - font-size: 24px; - line-height: 118%; - letter-spacing: -0.24px; - margin: 0 0 8px; - @media (max-width: 1024px) { - font-size: 20px; - } -} -p { - margin: 0; -} - -code, -.counter { - font-family: var(--mono); - display: inline-flex; - border-radius: 4px; - color: var(--text-h); -} - -code { - font-size: 15px; - line-height: 135%; - padding: 4px 8px; - background: var(--code-bg); -} diff --git a/appointment-booking/src/main.tsx b/appointment-booking/src/main.tsx index bef5202a3..4aff0256e 100644 --- a/appointment-booking/src/main.tsx +++ b/appointment-booking/src/main.tsx @@ -1,6 +1,5 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' -import './index.css' import App from './App.tsx' createRoot(document.getElementById('root')!).render( From 4b138566be2ba88a588ac494daf489cd402a9024 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:08:54 -0700 Subject: [PATCH 50/81] dev-44: add intial message to homepage --- appointment-booking/src/App.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/appointment-booking/src/App.tsx b/appointment-booking/src/App.tsx index 6bf84467c..0ac3f01ae 100644 --- a/appointment-booking/src/App.tsx +++ b/appointment-booking/src/App.tsx @@ -1,5 +1,10 @@ function App() { - return
+ return ( +
+

Appointment Booking

+

React boilerplate ready for development

+
+ ) } export default App From 95620990622cfd78520da49ab65a18ee45578c27 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:10:27 -0700 Subject: [PATCH 51/81] dev-44: update package-lock.json --- appointment-booking/package-lock.json | 577 ++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) diff --git a/appointment-booking/package-lock.json b/appointment-booking/package-lock.json index ee997d23b..4e1f7d55b 100644 --- a/appointment-booking/package-lock.json +++ b/appointment-booking/package-lock.json @@ -26,6 +26,7 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "license-checker": "^25.0.1", "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", @@ -1187,6 +1188,13 @@ } } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1227,6 +1235,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -1244,6 +1265,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-includes": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", @@ -1365,6 +1396,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1532,6 +1570,48 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1640,6 +1720,17 @@ } } }, + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1693,6 +1784,17 @@ "node": ">=8" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2334,6 +2436,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2467,6 +2576,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2480,6 +2611,37 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/globals": { "version": "17.6.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", @@ -2523,6 +2685,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -2536,6 +2705,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -2624,6 +2803,13 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2644,6 +2830,25 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -3100,6 +3305,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3167,6 +3379,48 @@ "node": ">= 0.8.0" } }, + "node_modules/license-checker": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "chalk": "^2.4.1", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "read-installed": "~4.0.3", + "semver": "^5.5.0", + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-satisfies": "^4.0.0", + "treeify": "^1.1.0" + }, + "bin": { + "license-checker": "bin/license-checker" + } + }, + "node_modules/license-checker/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/license-checker/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -3493,6 +3747,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3552,6 +3829,72 @@ "dev": true, "license": "MIT" }, + "node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true, + "license": "ISC" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3660,6 +4003,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3678,6 +4031,38 @@ "node": ">= 0.8.0" } }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -3738,6 +4123,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -3903,6 +4298,63 @@ "dev": true, "license": "MIT" }, + "node_modules/read-installed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/read-installed/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4231,6 +4683,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "*" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4241,6 +4703,73 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true, + "license": "(MIT AND CC-BY-3.0)" + }, + "node_modules/spdx-satisfies": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", + "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -4353,6 +4882,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -4399,6 +4941,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -4616,6 +5168,24 @@ "punycode": "^2.1.0" } }, + "node_modules/util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vite": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz", @@ -4809,6 +5379,13 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", From 6594f957ab0ac407a95d3cf3f66cac495e1fc381 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:21:31 -0700 Subject: [PATCH 52/81] dev-44: subtask 2 - organize app structure and integrate BC Gov design system - Create folder structure (components, services, models, utils, hooks) - Install @bcgov/design-system-react-components and @bcgov/design-tokens - Create Layout, Page, and Button wrapper components - Add BC Gov branding and styling (header, footer, theme colors) - Update license-check to include 0BSD (permissive) - All quality gates pass: type-check, lint, license-check, build --- appointment-booking/package-lock.json | 987 +++++++++++++++++- appointment-booking/package.json | 4 +- appointment-booking/src/App.css | 4 + appointment-booking/src/App.tsx | 12 +- .../src/components/common/Button.tsx | 6 + .../src/components/common/Layout.tsx | 23 + .../src/components/common/Page.tsx | 15 + .../src/components/common/index.ts | 3 + appointment-booking/src/index.css | 80 ++ appointment-booking/src/main.tsx | 1 + 10 files changed, 1121 insertions(+), 14 deletions(-) create mode 100644 appointment-booking/src/App.css create mode 100644 appointment-booking/src/components/common/Button.tsx create mode 100644 appointment-booking/src/components/common/Layout.tsx create mode 100644 appointment-booking/src/components/common/Page.tsx create mode 100644 appointment-booking/src/components/common/index.ts diff --git a/appointment-booking/package-lock.json b/appointment-booking/package-lock.json index 4e1f7d55b..6e3094034 100644 --- a/appointment-booking/package-lock.json +++ b/appointment-booking/package-lock.json @@ -8,6 +8,8 @@ "name": "appointment-booking", "version": "0.0.0", "dependencies": { + "@bcgov/design-system-react-components": "^0.7.0", + "@bcgov/design-tokens": "^4.0.0", "react": "^19.2.6", "react-dom": "^19.2.6" }, @@ -33,6 +35,68 @@ "vite": "^8.0.12" } }, + "node_modules/@adobe/react-spectrum": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.47.0.tgz", + "integrity": "sha512-EDQuMzz0kUeiMUUlxoeLFQyyxOXaAC7qlBw2PYOUfFLYd87xcV7VVV0JxiYx8zGk1IIY3UgQHgXrS1fv7CgezQ==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.1", + "@react-types/shared": "^3.34.0", + "@spectrum-icons/ui": "^3.7.0", + "@spectrum-icons/workflow": "^4.3.0", + "@swc/helpers": "^0.5.0", + "client-only": "^0.0.1", + "clsx": "^2.0.0", + "react-aria": "3.48.0", + "react-aria-components": "1.17.0", + "react-stately": "3.46.0", + "react-transition-group": "^4.4.5", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@adobe/react-spectrum-ui": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-ui/-/react-spectrum-ui-1.2.1.tgz", + "integrity": "sha512-wcrbEE2O/9WnEn6avBnaVRRx88S5PLFsPLr4wffzlbMfXeQsy+RMQwaJd3cbzrn18/j04Isit7f7Emfn0dhrJA==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@adobe/react-spectrum-workflow": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-workflow/-/react-spectrum-workflow-2.3.5.tgz", + "integrity": "sha512-b53VIPwPWKb/T5gzE3qs+QlGP5gVrw/LnWV3xMksDU+CRl3rzOKUwxIGiZO8ICyYh1WiyqY4myGlPU/nAynBUg==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@adobe/react-spectrum/node_modules/react-aria-components": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.17.0.tgz", + "integrity": "sha512-0EyisMgvsFJ2aML3crDYv2tW5vT2Ryf8PGzY/g63JjDdCbLshlwazhS8JNtPF1vkTkungJJ6sVJbKyX+YKSoFA==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.1", + "@react-types/shared": "^3.34.0", + "@swc/helpers": "^0.5.0", + "client-only": "^0.0.1", + "react-aria": "3.48.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -225,6 +289,15 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -273,6 +346,36 @@ "node": ">=6.9.0" } }, + "node_modules/@bcgov/design-system-react-components": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@bcgov/design-system-react-components/-/design-system-react-components-0.7.0.tgz", + "integrity": "sha512-WiAqos9Tv8WxAzccpN/HnKj7oDF1neX2rOX8hfKFsA8BBitjbYbvDTjv9eCVdVwkqxw9RyJ5bvoUIrHiwkPU5w==", + "license": "Apache-2.0", + "dependencies": { + "@bcgov/design-tokens": "3.2.0", + "react-aria-components": "1.16.0" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "*" + }, + "peerDependencies": { + "@bcgov/bc-sans": "^2.1.0", + "react": "^16.14.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.14.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@bcgov/design-system-react-components/node_modules/@bcgov/design-tokens": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@bcgov/design-tokens/-/design-tokens-3.2.0.tgz", + "integrity": "sha512-2D3mPFKTpNLeIp8ER9y8oDKxXE8+rBfcvuF4yFd7uUvDm0izZNFhambUi6sN3OdkmuZoGQO7AjlMtOQzsHtxzA==", + "license": "Apache-2.0" + }, + "node_modules/@bcgov/design-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@bcgov/design-tokens/-/design-tokens-4.0.0.tgz", + "integrity": "sha512-kcen18Q/snP6M6JQ6XUQTlWXM1Gnm6y8Zk0i29K+T/Y0m/ab4PufmGpl43l/EJKCnYgpOaZFSXDK7CCO6P0INw==", + "license": "Apache-2.0" + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -435,6 +538,57 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", + "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.2", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -501,6 +655,43 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@internationalized/date": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.1.tgz", + "integrity": "sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/message": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.9.tgz", + "integrity": "sha512-x03MSVTaB/4JHtW1VAYaY/2cCuBrHbWM6ZvlgpKdnSdW28tZbqpR673RJrVJyXWRw1bpgYN89Tz7ohX5tgNgPA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "intl-messageformat": "^10.1.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.6.tgz", + "integrity": "sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/string": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.8.tgz", + "integrity": "sha512-NdbMQUSfXLYIQol5VyMtinm9pZDciiMfN7RtmSuSB78io1hqwJ0naYfxyW6vgxWBkzWymQa/3uLDlbfmshtCaA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -593,6 +784,580 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@react-aria/autocomplete": { + "version": "3.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@react-aria/autocomplete/-/autocomplete-3.0.0-rc.6.tgz", + "integrity": "sha512-uymUNJ8NW+dX7lmgkHE+SklAbxwktycAJcI5lBBw6KPZyc0EdMHC+/Fc5CUz3enIAhNwd2oxxogcSHknquMzQA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/combobox": "^3.15.0", + "@react-aria/focus": "^3.21.5", + "@react-aria/i18n": "^3.12.16", + "@react-aria/interactions": "^3.27.1", + "@react-aria/listbox": "^3.15.3", + "@react-aria/searchfield": "^3.8.12", + "@react-aria/textfield": "^3.18.5", + "@react-aria/utils": "^3.33.1", + "@react-stately/autocomplete": "3.0.0-beta.4", + "@react-stately/combobox": "^3.13.0", + "@react-types/autocomplete": "3.0.0-alpha.38", + "@react-types/button": "^3.15.1", + "@react-types/shared": "^3.33.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/button": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.15.0.tgz", + "integrity": "sha512-p8KehQ+OmhvhYmsjkp4K/Yv0tufyEBOHu6woJlRYL6kq5m6GKY5MZp8pyO26FpSiOyjhnZe6wbTyvCifvaokwQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/collections": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@react-aria/collections/-/collections-3.1.0.tgz", + "integrity": "sha512-OwWoxHaAN0jNw7oo17TfTPY2KZksDme4p5a8KCnNBfm/a0zo+y4wiOUaFpGDmDT6HxwznyBcu3x3oSN7orTtvA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/combobox": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.16.0.tgz", + "integrity": "sha512-yPPLduVUluCBuIFSZ+WgBMgl5oH7A5oQTemSpvf9ZmJ2oABIe+h6VQcwkEtMdokveHrdbmWIB9BL0E5jdDczAA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/dnd": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.12.0.tgz", + "integrity": "sha512-FqAaxIMCgpq83UQhJRpSR+o3K4XQQYbJMKxhBWGL84Wn1p6m8QmWGvRKAiHeUoQXlY8R6KNxlMR/KjJvV8/lBA==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.34.0", + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/focus": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.22.0.tgz", + "integrity": "sha512-ZfDOVuVhqDsM9mkNji3QUZ/d40JhlVgXrDkrfXylM1035QCrcTHN7m2DpbE95sU2A8EQb4wikvt5jM6K/73BPg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/i18n": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.13.0.tgz", + "integrity": "sha512-APjw4EwmvlnIyDxixSWfjHvOFFkW2rVTyKZ4l9FV0v7hOerh+FWLE6mF1XnnX3pgz3yARkKWwhSR9xYcRK6tpg==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.1", + "@internationalized/message": "^3.1.9", + "@internationalized/string": "^3.2.8", + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.28.0.tgz", + "integrity": "sha512-OXwdU1EWFdMxmr/K1CXNGJzmNlCClByb+PuCaqUyzBymHPCGVhawirLIon/CrIN5psh3AiWpHSh4H0WeJdVpng==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.34.0", + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/listbox": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.16.0.tgz", + "integrity": "sha512-Jv6aTJECRntBvG+0ZQtXniAtHEQjvEi2QSm35FxRcsB8kgv7TmcinUOSZuHe5r8RDY2djILwdrqmfy6ApX0MDA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/live-announcer": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.5.0.tgz", + "integrity": "sha512-1b+Txq00WQ/PJPCsZT+CI5qP86DrfFGPuJL5ifKtdMVXrxNGJWrfu7jTj6q9AbAOOXLG11BJ6blILu7sZeRPxg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/overlays": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.32.0.tgz", + "integrity": "sha512-H9meBB14/M0bDwk8gZl8Fu8bwZN2El9LDlk5cNkgAozbEiRuQvTFOeE3RoP6XI6bwEnSBvb0ovPmx3/kNyOehQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/searchfield": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.9.0.tgz", + "integrity": "sha512-B8oOFhwGDotb7enoQ0bmXjhVkx8BkzK/ZnMmr4aF2ezexSFpG/ELkWqJPlfwknCwoBgBzhmpIKZvkoxQHR/iIw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.10.0.tgz", + "integrity": "sha512-mnelvACtfNWWKFCT1YHebxJRmfBmmANGwHQhCFPByMVTx1L8RumcaLxChYkE87g2KPuP5xX2il/oRn1DytW+qQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/textfield": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.19.0.tgz", + "integrity": "sha512-P5Da8QFV/bCp3oCXQAqaTWhXNtx4vWEjvoqa49oG5TM1blodLjFrzNyiRM7TmQU0VLwiQPAQrqD4yaDLXZ0Nqg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/toolbar": { + "version": "3.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.24.tgz", + "integrity": "sha512-B2Rmpko7Ghi2RbNfsGdbR7I+RQBDhPGVE4bU3/EwHz+P/vNe5LyGPTeSwqaOMsQTF9lKNCkY8424dVTCr6RUMg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/focus": "^3.21.5", + "@react-aria/i18n": "^3.12.16", + "@react-aria/utils": "^3.33.1", + "@react-types/shared": "^3.33.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.34.0.tgz", + "integrity": "sha512-ZM1ZXIqpwGTJjjL6o3JhlZkEaBpQdxuOCqLEvwEwooaj5GsYI3E9UfOl5vy3UW6bYiEEWl9pNBntrb9CR9kItQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/virtualizer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@react-aria/virtualizer/-/virtualizer-4.2.0.tgz", + "integrity": "sha512-j4Dj/PMQK+d/2E2Dxyr5ifPdg8IG/NYsfZJtPnvKHXeKlI3vj7F/InamujwCuEFHZBK3YmxlRPRGcoMdS66qfw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "3.48.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-spectrum/button": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/button/-/button-3.18.0.tgz", + "integrity": "sha512-SmjsXt+mLK2cf8PGstNZvLBfjqE5TjHW15yPIATI6ddqMdcC9JZ3ldnBTdFji9P/B6Rlop4ajnAdDV6bpxLtXA==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-spectrum/combobox": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/combobox/-/combobox-3.17.0.tgz", + "integrity": "sha512-HuJjm7m5W/lvpak0KQXAdqkvO17GpIfQ6/AuddnXEyltWhl481oRok04b9VQKdZAy9xrmmpReFcvXJ4zFDIGvg==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-spectrum/form": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/form/-/form-3.8.0.tgz", + "integrity": "sha512-zNFA1zRFvoEZAhK6HxWyo2yUDlUXqCaaySxHvuCaevq4Zw3DKndbxq72ax9YDaUAvYlhgnb/bI616JBCZUAhdA==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-spectrum/searchfield": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/searchfield/-/searchfield-3.9.0.tgz", + "integrity": "sha512-so1IeolnYPsgjoIJmoGakUuQv+Ij5NVoWx2VTBc3o2sUva1GkxtFp6Woiow2Qqw0mFyoTwwoKi6zQJHLMqHhvQ==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-spectrum/table": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/table/-/table-3.18.0.tgz", + "integrity": "sha512-ckvjjz/Tp8Y/Ck/OOOXX/dTs8PtoWCy7+QDBxWzNpqvxcLiLviudNKJFhGfeyGGDi3IOMWRSaG5yWN9wrvItNQ==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/autocomplete": { + "version": "3.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@react-stately/autocomplete/-/autocomplete-3.0.0-beta.4.tgz", + "integrity": "sha512-K2Uy7XEdseFvgwRQ8CyrYEHMupjVKEszddOapP8deNz4hntYvT1aRm0m+sKa5Kl/4kvg9c/3NZpQcrky/vRZIg==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/utils": "^3.11.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/combobox": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.14.0.tgz", + "integrity": "sha512-WYWJK9IzWKDqnFk1HF6FGISrhPh/bzYkRLgd40Psyke5dwYusOrEUzG161P8v9vxAzs621pYB4hz2UhrRWsMPQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/grid": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.12.0.tgz", + "integrity": "sha512-MLCN3hyxRpaj4tUnQyc+aFR2/QBP2rmFcXGdADHO1c0kvxd9Vk+bEe88uA9MJTz2VClQ+AN7yRGCTIuME+QvNQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/layout": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@react-stately/layout/-/layout-4.7.0.tgz", + "integrity": "sha512-63VvOKI8KXeD3SO7yt41511CdZ+9wK2LVY2mpY+c87ML5fVVgMTc/o1IsYWI07rW3IjEFcWoW93eItgMbJCXyg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/searchfield": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.6.0.tgz", + "integrity": "sha512-cSSDXynj3oQdILHSthEaB2pAIXG0POHKqjZZ07k6L/A0fkyhkwAbR/hyg+idHCH7Ah9hWnS+YhicWE1b6nw1zg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/selection": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.21.0.tgz", + "integrity": "sha512-SBafuZoJgDr7L9dCgMjat1Mx85xOnRO7E5UCtAj6TF/7yhisJVnrNkfnryOH2CunPyXCUgfUlUebkxeXjbW+lw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/table": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.16.0.tgz", + "integrity": "sha512-xuvkFuRj8SYE36T+hEed+30oPCzbkcGNduVBAOJGr9K0z7y/a3OxFdOnAv/OsCvKW1sLK231oTSFvZ36zRFndA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.12.0.tgz", + "integrity": "sha512-7q+iHz9cENvro1dVKgdTxNh1i1mtWuLUI6UHp10TAgpxM9DyRDvmuN35zLXYCmMDgx3WLY2xkwqoez8xd+CdxQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/virtualizer": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-4.5.0.tgz", + "integrity": "sha512-Yf1xA9U7PuuaHgxWExwO5MrWCqzDZZTPCzo9YCcsilkOFk2GxMcGNysM71funj+d4CMqsWjCNmgNhZQjBXKjNw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-stately": "3.46.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/autocomplete": { + "version": "3.0.0-alpha.38", + "resolved": "https://registry.npmjs.org/@react-types/autocomplete/-/autocomplete-3.0.0-alpha.38.tgz", + "integrity": "sha512-0XrlVC8drzcrCNzybbkZdLcTofXEzBsHuaFevt5awW1J0xBJ+SMLIQMDeUYrvKjjwXUBlCtjJJpOvitGt4Z+KA==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/combobox": "^3.14.0", + "@react-types/searchfield": "^3.6.8", + "@react-types/shared": "^3.33.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/button": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.16.0.tgz", + "integrity": "sha512-Z5///n2Y1jtF0gokBq2Y1K1cpOwsWZ24HPeAm3eEmZrbBXMrxC2oEA5ZThsSHuIGsqiyNJiQ2scsDftmr+PkZw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/button": "^3.15.0", + "@react-spectrum/button": "^3.18.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/combobox": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.15.0.tgz", + "integrity": "sha512-iWV9UfLg1P0XhEqPTbnhsVMHFwc0RnrZjHfCLwgilH0Af0z1CQ8RyWiT8cOd1eqbkOAiVgCv29Xs8PAxaQBHSg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/combobox": "^3.16.0", + "@react-spectrum/combobox": "^3.17.0", + "@react-stately/combobox": "^3.14.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/form": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@react-types/form/-/form-3.8.0.tgz", + "integrity": "sha512-ff38E4/5xLxqVemicLw+GefRoWBJEAro+hDwFZ5sde6kslktYt2LHJ7+IkID6yQYy+T3qxXgIdfxX+O1rlpWvw==", + "license": "Apache-2.0", + "dependencies": { + "@react-spectrum/form": "^3.8.0", + "@react-types/shared": "^3.34.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/grid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.4.0.tgz", + "integrity": "sha512-h+u3hKli9gVwfYx6cabkTNZrP+HQ97vAmTugGIk5IAfouE6kjhoDaDzVD0VUvIWqc12LIkrqe1LdBMZ0ofbV6A==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/grid": "^3.12.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/searchfield": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.7.0.tgz", + "integrity": "sha512-/JBVYkXLB4EozPEfgpYW7C9tzw4xUmdVmuf7g+8ip/AsFgsyW/FjdLpbaXESXR78D0nppWaNxUxcdUXcuo+eEg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/searchfield": "^3.9.0", + "@react-spectrum/searchfield": "^3.9.0", + "@react-stately/searchfield": "^3.6.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.34.0.tgz", + "integrity": "sha512-gp6xo/s2lX54AlTjOiqwDnxA7UW79BNvI9dB9pr3LZTzRKCd1ZA+ZbgKw/ReIiWuvvVw/8QFJpnqeeFyLocMcQ==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/table": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.14.0.tgz", + "integrity": "sha512-emTYu9biFFlVEN208EmYpnO3bzi0f7q+07rnerUWRRRqXQpgNW/G5Tc6ifgCUXLp+KjwzLIeoDl4hs3ZnG5NSA==", + "license": "Apache-2.0", + "dependencies": { + "@react-spectrum/table": "^3.18.0", + "@react-stately/table": "^3.16.0", + "@react-types/shared": "^3.34.0" + }, + "peerDependencies": { + "@react-spectrum/provider": "^3.0.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", @@ -857,6 +1622,59 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@spectrum-icons/ui": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.7.0.tgz", + "integrity": "sha512-86iQSDfJb3Ama1WSJ/mEiFy4DJT7e/v4pSmEuX4aKKMzbNYft+O40N18S2POUnmblrb7MQneLC/pgIp1SDBwEQ==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum-ui": "1.2.1", + "@babel/runtime": "^7.24.4", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "@adobe/react-spectrum": "^3.47.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@spectrum-icons/workflow": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.3.0.tgz", + "integrity": "sha512-ILuhgWh9jMXaEVMRuOYgTAjMc22cKyvCtUDyZmc8OEMfOYuejj+Gcp5t6DhaCfE0M9rORtVxCrRgsO2WyEgfUw==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/react-spectrum-workflow": "2.3.5", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "@adobe/react-spectrum": "^3.47.0", + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", + "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -1248,6 +2066,18 @@ "node": ">=4" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -1595,6 +2425,21 @@ "node": ">=0.8.0" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1645,7 +2490,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/data-view-buffer": { @@ -1731,6 +2575,12 @@ "node": "*" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1808,6 +2658,16 @@ "node": ">=0.10.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2864,6 +3724,18 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -3282,7 +4154,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/jsesc": { @@ -3702,7 +4573,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -3899,7 +4769,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4252,7 +5121,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4279,6 +5147,68 @@ "node": ">=0.10.0" } }, + "node_modules/react-aria": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.48.0.tgz", + "integrity": "sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.1", + "@internationalized/number": "^3.6.6", + "@internationalized/string": "^3.2.8", + "@react-types/shared": "^3.34.0", + "@swc/helpers": "^0.5.0", + "aria-hidden": "^1.2.3", + "clsx": "^2.0.0", + "react-stately": "3.46.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-aria-components": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.16.0.tgz", + "integrity": "sha512-MjHbTLpMFzzD2Tv5KbeXoZwPczuUWZcRavVvQQlNHRtXHH38D+sToMEYpNeir7Wh3K/XWtzeX3EujfJW6QNkrw==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.0", + "@internationalized/string": "^3.2.7", + "@react-aria/autocomplete": "3.0.0-rc.6", + "@react-aria/collections": "^3.0.3", + "@react-aria/dnd": "^3.11.6", + "@react-aria/focus": "^3.21.5", + "@react-aria/interactions": "^3.27.1", + "@react-aria/live-announcer": "^3.4.4", + "@react-aria/overlays": "^3.31.2", + "@react-aria/ssr": "^3.9.10", + "@react-aria/textfield": "^3.18.5", + "@react-aria/toolbar": "3.0.0-beta.24", + "@react-aria/utils": "^3.33.1", + "@react-aria/virtualizer": "^4.1.13", + "@react-stately/autocomplete": "3.0.0-beta.4", + "@react-stately/layout": "^4.6.0", + "@react-stately/selection": "^3.20.9", + "@react-stately/table": "^3.15.4", + "@react-stately/utils": "^3.11.0", + "@react-stately/virtualizer": "^4.4.6", + "@react-types/form": "^3.7.18", + "@react-types/grid": "^3.3.8", + "@react-types/shared": "^3.33.1", + "@react-types/table": "^3.13.6", + "@swc/helpers": "^0.5.0", + "client-only": "^0.0.1", + "react-aria": "^3.47.0", + "react-stately": "^3.45.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/react-dom": { "version": "19.2.6", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", @@ -4295,9 +5225,41 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, + "node_modules/react-stately": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.46.0.tgz", + "integrity": "sha512-OdxhWvHgs2L4OJGIs7hnuTr5WjjMM6enhNEAMRqiekhF8+ITvA2LRwNftOZwcogaoCslGYq5S2VQTQwnm0GbCA==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.1", + "@internationalized/number": "^3.6.6", + "@internationalized/string": "^3.2.8", + "@react-types/shared": "^3.34.0", + "@swc/helpers": "^0.5.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-installed": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", @@ -4968,9 +5930,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -5168,6 +6128,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-extend": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", diff --git a/appointment-booking/package.json b/appointment-booking/package.json index 37e64d034..31d6967a8 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -11,9 +11,11 @@ "format:check": "prettier --check src", "type-check": "tsc --noEmit", "preview": "vite preview", - "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause\" --excludePackages \"appointment-booking@0.0.0\"" + "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0\"" }, "dependencies": { + "@bcgov/design-system-react-components": "^0.7.0", + "@bcgov/design-tokens": "^4.0.0", "react": "^19.2.6", "react-dom": "^19.2.6" }, diff --git a/appointment-booking/src/App.css b/appointment-booking/src/App.css new file mode 100644 index 000000000..8e9b847e8 --- /dev/null +++ b/appointment-booking/src/App.css @@ -0,0 +1,4 @@ +/* App-specific styles - keep minimal */ +#app { + width: 100%; +} diff --git a/appointment-booking/src/App.tsx b/appointment-booking/src/App.tsx index 0ac3f01ae..0eae59980 100644 --- a/appointment-booking/src/App.tsx +++ b/appointment-booking/src/App.tsx @@ -1,9 +1,13 @@ +import { Layout, Page } from '@/components/common' +import './App.css' + function App() { return ( -
-

Appointment Booking

-

React boilerplate ready for development

-
+ + +

React boilerplate with BC Gov Design System ready for development

+
+
) } diff --git a/appointment-booking/src/components/common/Button.tsx b/appointment-booking/src/components/common/Button.tsx new file mode 100644 index 000000000..15489ce0e --- /dev/null +++ b/appointment-booking/src/components/common/Button.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import { Button as DSButton } from '@bcgov/design-system-react-components' + +export function Button(props: React.ComponentPropsWithoutRef) { + return +} diff --git a/appointment-booking/src/components/common/Layout.tsx b/appointment-booking/src/components/common/Layout.tsx new file mode 100644 index 000000000..76e81b2d9 --- /dev/null +++ b/appointment-booking/src/components/common/Layout.tsx @@ -0,0 +1,23 @@ +import React from 'react' + +interface LayoutProps { + children: React.ReactNode +} + +export function Layout({ children }: LayoutProps) { + return ( +
+
+
+

BC Government

+
+
+
{children}
+
+
+

© 2026 Province of British Columbia

+
+
+
+ ) +} diff --git a/appointment-booking/src/components/common/Page.tsx b/appointment-booking/src/components/common/Page.tsx new file mode 100644 index 000000000..e668a60b7 --- /dev/null +++ b/appointment-booking/src/components/common/Page.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +interface PageProps { + title: string + children: React.ReactNode +} + +export function Page({ title, children }: PageProps) { + return ( +
+

{title}

+
{children}
+
+ ) +} diff --git a/appointment-booking/src/components/common/index.ts b/appointment-booking/src/components/common/index.ts new file mode 100644 index 000000000..8340ea00e --- /dev/null +++ b/appointment-booking/src/components/common/index.ts @@ -0,0 +1,3 @@ +export { Layout } from './Layout' +export { Button } from './Button' +export { Page } from './Page' diff --git a/appointment-booking/src/index.css b/appointment-booking/src/index.css index 8b1378917..6a50fd086 100644 --- a/appointment-booking/src/index.css +++ b/appointment-booking/src/index.css @@ -1 +1,81 @@ +:root { + font-family: 'BC Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + line-height: 1.5; + font-weight: 400; + color: rgba(0, 0, 0, 0.87); + background-color: #f5f5f5; +} +body { + margin: 0; + display: flex; + min-width: 320px; + min-height: 100vh; +} + +#root { + width: 100%; +} + +h1 { + margin: 0.5rem 0; + font-size: 2rem; + font-weight: 700; +} + +p { + margin: 1rem 0; +} + +.layout-wrapper { + display: flex; + flex-direction: column; + min-height: 100vh; + width: 100%; +} + +.layout-header { + background-color: #003366; + color: white; + padding: 1rem 2rem; + border-bottom: 4px solid #fcba19; +} + +.header-content { + max-width: 1200px; + margin: 0 auto; +} + +.header-content h1 { + color: white; + margin: 0; + font-size: 1.5rem; +} + +.layout-main { + flex: 1; + padding: 2rem; + max-width: 1200px; + margin: 0 auto; + width: 100%; +} + +.layout-footer { + background-color: #f5f5f5; + border-top: 1px solid #ddd; + padding: 2rem; + text-align: center; + font-size: 0.875rem; + color: #666; +} + +.page { + background: white; + border-radius: 4px; + padding: 2rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.page-content { + margin-top: 1rem; +} diff --git a/appointment-booking/src/main.tsx b/appointment-booking/src/main.tsx index 4aff0256e..bef5202a3 100644 --- a/appointment-booking/src/main.tsx +++ b/appointment-booking/src/main.tsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import './index.css' import App from './App.tsx' createRoot(document.getElementById('root')!).render( From 63b8ce578e9a688fd9f46113a1b25385525a197f Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:29:09 -0700 Subject: [PATCH 53/81] dev-44: use local BC Gov logo and favicon assets from appointment-frontend --- appointment-booking/index.html | 2 +- appointment-booking/public/favicon.png | Bin 0 -> 1129 bytes .../src/assets/img/gov3_bc_logo.png | Bin 0 -> 3603 bytes .../src/assets/img/gov3_bc_logo_mobile.png | Bin 0 -> 3521 bytes .../src/components/common/Layout.tsx | 7 ++++++- appointment-booking/src/index.css | 6 ++++++ 6 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 appointment-booking/public/favicon.png create mode 100644 appointment-booking/src/assets/img/gov3_bc_logo.png create mode 100644 appointment-booking/src/assets/img/gov3_bc_logo_mobile.png diff --git a/appointment-booking/index.html b/appointment-booking/index.html index 84fad34e0..239f82461 100644 --- a/appointment-booking/index.html +++ b/appointment-booking/index.html @@ -2,7 +2,7 @@ - + appointment-booking diff --git a/appointment-booking/public/favicon.png b/appointment-booking/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2ffb7bc1e3cd9f5aa916fca67a1c348d025aa3 GIT binary patch literal 1129 zcmdT?O-NKx6#mYA?~SA5ym5vxQ%5GV$RQ;{2q6-*5gjCn2ttBL3#y&a!bJqpA_$47 z2&@*hk#-Vo%nGci2ufQNWl0$m^fOvEIy27Cd+&Chl#S9l=)$=_=X~e;?m73K-r3pa zE~+R3;EuG1ckxQ>Xm?683{MrFP3C6Ch_shNI4@A^`)Gd75`mN;bHslDRB%f1G?$v036NF> zD8-NqlgtxBzhC`LvP59qt-VjG-Xs+b!WV`l0x2%WDUjJ1NOi-C-2mm{ zAZd<@)^{y7$s&O{>^~$`*MKPB$1V%xt_NIeIjRCe0dfh*#9PR@ry#P4t^-E0TaA3X zMsm1zjp-CVq3%p1IZ6SItJ_0AmooHTyHv_HjR*SY_Hli zFwwLrskb@E70P;j6%Obd_~y>V+{H=o*K(FJQ5-PSkj`~D|5%@OJLY1l?@Dhtk=25@ zo#E?6jmw7vGY2~qe=1|3=iIHRWkL4$Y^w^c@`#%+toV^jNX(}SQuEpR^(5-TgpM<> zSS9pF4%ix6J@ssI+MO{>|1ZB)U8aAW`Z^cP$MJq#<(3L=`7-~b*I%laIk=U7NoI%? zDlE|lA#Qw1kp``mdiiZ~pv0@1Hq$-*whrXYI4k%$>P0hWZ-xG+Z*O)O9rdb)5&Z%+w(gtvo}M6jn1kOzU}m4bcj9o?N!Utxa&gr}`#Bk-^-Ubn?vAnuekDa3`CvF;;OT_22M2q4c=^MF z75M+{3kUEY8p03$w*=*`z_0wLAlO3B5UlF$=LD9KfQdUwUXub}mz9u&L1m?`iGih{ zlGh>7>kvt4ai}C53WH0_g8v%)iZoz(KZG;fNKO5(V!%p)-vx#8fkPlcK|vBh(h}Z& zNQk7YtSkg71(A{x2NdG|Azmo^U~w;hfj<^!9%@ehj()B_C|7SU@E=cm2k!us0zW_G zPZ7vpRe<&&fA1(2uzGq$Q({)hWVqAttuA9Mel*skaP=j182MezS*ffTk0d?whgVLbx| zULXEqYB|ope?>#@0`s5}vw&i3tDsF-Ikrt05d3GSxHb`h!F56Y-e?w30@wo6GN|M( zy6g-C(b%^T&;Sm&PVqA_w)OHKrjh4tY3x^cCU%OdHbE7iVJmc*ZFt2_x8#2ob$jy3 zJ*M`I*{05|{`m;iW?@N%*03a(QE+bRTDouz=d(=D|>;jmrLMVNiGKaJX|VZ+aa z?~PmIzX$D&817GbA8z&Te%1sw5`M<)^vmOaM4p};5$02YlMsJpoE#H&2UYgQOtG!P z+qLY(U$20=$NO^!tL22bq@%r=eT+MCx!{6HaeOPEVJ#EuY`-eQ}04}RBK zp+C+nY@lDX3}cq|F|PRU_Xuzr8R6yLI3*JUSY%d32|d*kKp>GX0#>NA)<15 z@?(AH*c_&13NuQ;&3AV-9UdK*mlscreU>V zZ#9Sh%vz+{{c>($s%L{Nh`9ATO(cwTr0bg+VW;Ec8&8Db(DHdkaxC=?r*GWy*L>D=g_CLo*0`GKTg>2~xpM06 z7fkTwNeDk@ZF)tu5o|Z|eBV(wvcG!#{bQR)G9uEFd5y&CW2t_eI;vgPgwU#0F2 z7tQUDNRFqnyauCJxqg0ay|Gvdnrm?6#x%S+P-GXVF@Ai^_6VBYH8K0t+c&>uX1IsW z%qch?BNKJkOANnWB)3&xB(zdQbyvXbyTzfJkkokY2+T^(t%@(eUr)}=N{1O%R?gS< zY^s+V&)Tne8qzAr`KHJ3n+xR97f{qsC7Z>Ws6K?r z7@6Dmj1{6ysk5)KGs+U`X=oy#JukX+Xf6oRl58`k&^jCiR(2{I=`(%OWjW7zmHoLV z*-upZwaXYQs?V`x^CoYDvLl!m>({1{@l6?`s(Z2??S6SVTx%-*UM3@MFmNT4%qM#L z4u@~%by)Qxvs*VU zoDTMMx8zdp;_xWx?={nHZB=|8gXv4(v@uSKJ$l5Z7Nf_Hf#Z}(a=A-~nh3r?*iXB2 zfi)r)Z0+B1ZxS_XcvHvtg5S-+U@kYs157_uBQK0s^HW_NGF13FmfLKUuh!j`p-gZ5 zxqsH@i#mBXndeuEN1#=F|1><6Y*RbD8=N z&~}xqKPuYa!#}Jxc&hgo)RZo1%a_EkoU-zG-qCH7d8Eg!rZpOYgn`A9lw*{Jx>K0# z8!9#gGT&%7dK&M1HHoh1nkw;%3K*6!3pQ4uP8EN)+p^cqhiVARCnfSRF zXn-P&n_)G{S_LyIk3pHMs0zz^ipbp17B4ALSiWrci4{KJ>EgBKvQaz!=xe>G%EW7U z<$!>dAVS(BQ^c&d(E}u?d=h$6V^B~s`qVKqTYBMW#1gqNtjb=%S#c*58-f5WJDa>Q z&Oqbq639pME~vFr@Y_{LTQy2sQO7tZQSb_SO!8oG@Qd+mO^Z_dbv4rqE9#d}5DLUP z%#j>lV_Vv6;neTrq!Y>VLomAwC(OyQ9J>OFwYZA;oJM0Vd5NU+AWu_jAjOG!apN7Y zMyo<4d#GcJjclRuIHq(y#VK-zo(u2tZB5ROJ#Zi+gR4Z#{C3MJ(tm`94q0v%tJe4= z6#Pru;sL2{2ZPNb#Hq52G1-?|E>yfcgduaNkL;oa?)42DNf0F|#_#Pgm2FL@`$TMu zs$1B%Sw*fg`iyCGazBki97Q!|(7Gl(VeKRD%80>MwQp@9y zwnfF;@y>?VXJMJbQ>6@z=0&x;J&oxyD*KO}H$rfmyb0llUv_BjxM63X1dOIH;RU4f z8=k@meZ8~Mq9^jYUry)n>pBQZ3NkDs*=>^*Yv)6~l6Fh=tPw?mA$C(vADjq%{gA6TxR;ot5fdpOS;oXPn-Be7ZD}dN2mE=caXpzdm;Z&+eddt zNUV`-bKbF2l?ex|2PE6&TiiU7lLOP; zv*i}Er)YnWZWbPK*N!iD6KbJt(?h(qk`MUI3dmbxnp6zZP&a0mkX5SRrRJ91 zlJb=cIhEvCM?@L3Fry7rY(b=Nj$LcBT;rao#&)rku<7$#)3;xNuQ~)-s8`4^@l>a* zdYIPs-{YnK5@IB`?mT7u>lo63%FhDDtvB7ct8I<(>s^{xg_2%>6S_$^Z6!$5uO!CJ zK1fG>G{c_K8lN9<#;D9qt!{C7$g;yEhA7%C)jWxTuypp8CrwO)WsC0*paGmBn@I_b zi_#uUd2lITlDf@F?fx=y=QfQx5eK-!f`nJ$)q2`lZ+7DR=|b^x?J|2JK5+ zx`7VQpd~DsYr9v@tMri|nSCl&-Ppvkop*4Hez&0Cp0$V@)=d50f9zUStS41-{L(ri zv4R1n%rS;ukZ$dM*vZJi0z$&J-9t?(Dr2_CT-Z{#VEXK0)12SlArd96km!dG1EZ~m zo(lPL=^aq!(xz7?Dp9wHgtA;o?bAk9vi@YI>R^L7WeK!fhg;RkyDL=_`zKPglWxs< z8nhvfgq~fFX**a*`f@Yr0$nJkb22W>Rwz%4aDiDz;@4F^czj+Bi?+7DsbERhNEC$k z@f>5-3!Cp1#HifgiD3)rjv6_$jf*4E?4S2j1mqXY&~buq1s%RL87bFaqsZwRt0k?= zV(v{|fLzRZomy-sdE1*Wu}7RF`CY5h?ar9V6=snh-ig(^XdkKhzL2BE76a{EW0c*5LB5R5q%dA1D`IT$~LO@%-vujL>wO%8F&A5w4rb3#0torpJ8k2Fxa zK0xlC7#g-Sat}1fmsqTHw2NKnX~DJ6?d@3Nifn8BiA$rW25AT_MROdl^jx`^r2fk^ R;4cfLbxU8ZRK@PWzW^>bft&yU literal 0 HcmV?d00001 diff --git a/appointment-booking/src/assets/img/gov3_bc_logo_mobile.png b/appointment-booking/src/assets/img/gov3_bc_logo_mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..262b7e8ea268e35f38db80c49f26523d87d62f0e GIT binary patch literal 3521 zcmcgvX;c$g7EXX5tD-2YLOO`hts#|_P=X7D7L>3nAOB@ zf;0$mG?ClJYafCvIKkZO&c{^1a75)K#iWc)a7 z3;D9c@nkYFNFYQ+L|8?TtwfSw0@20>;UE!6Bs_w^OScJSP$XU`HT%NAfTe5+Uo7K` zgjfY56eJ3h+2IhSQ!WU^GqghKbeNEU5h5WmfoKINT$%)O*fThBm?U)4IEPJuLtz0d zlt~dRaRw{qiDV)vPxK$CXO{nB00}LVIb-9SS_Fa_6H=LDIO4{1K)#8VdT$fM1W#Bh z3X`y5$8bcZnZg?}=pcb18O%U#I~)lh0eFCdCs}(F=^&8?QiyoK8U%nTC{x7YbGLmB zrQ!h_Zv;mrfdKt4P$UC5kPP~-U=ADPiX;LE(a#q^!7xEA48~z+q5?XILPZk9Frtq9 z^}Mr#gS$k;<%c2*QqMK^SZ7BE8qtPE!;`FtlX96%&{-&zK|(g{%&@~DzFP759FPLj z=rF*g<0+7hHJ(D_1mWpKRhXogRhqv6Hy~LTp8jBawl^ zVGeG3lK+ip{4KlF`y+TTLOGQq-#qv!+for%76D1%)xk*QzSO?^mny)LkmA5vG9fl! zsHltYzo=vLAYm|!bR7bY@ISlr_YQYT=l|6mf}-6_9Tef;&v0_>tJZ{gKRJc;JH>FO z(IOu+tr`|0k&+;d*s!`^9fg{8$eFR)oA>Z#mVc=C`uTe-UN_9j*&oMKn`f`GAM=E% zl=vEB>cY@*ia#IANKxZq4;v*KF)i(}ixY^rlq-Q}wdbSr)C%}~HJoIro=U}iBX3@C z7e6?vyHwQJxtH9U8O{X@1R1}S+|INK9qg|8M|lvz~bJdX=km-zR@Z zvqSQSIY~LKz9a7?8ck2C?sNxnk5}3j-pCqf4Pkz;Fd6Xsyep+V#SOq9+Erfo>6A1~|kCTQfR^&z{v-{9PbgqxZcw=NFP@?WEGfl1d24Or#TFC3v;(eDQ%azDlA z4;r6Z;@q!ixFXxtwk&s^(uOnJ3ZJyBtr~iag+x&_N7P%(EH@#k`{`dT5-l_*998vT?miisOuyG*F zg;{$lUMVhArTMm}#mg9ys0?j<;T%z^OSxlw|L0zI;m2gm1(W8vPNF+6az4GW*f46? z2p`J(bUE3_;@4dI0$b{X8-kHJ$AhxSq+2I=lT6VVy;%t>9Fb?F}PHSCwOgfJ$;8z z?4_5CNy>^UUwD$0e_)R&x-3j^xbp{V>cgn=E9jbstoY%oBbVE0Yk0$`@KKP`MR~EE zeo`xNj`{B7n$nQtzuVu@9JrCDG}>m|P7kVk6!~FHPq%dZZljT_-zt@Y#+_@2^e@C# z5|-Ovn~?7uT~}W>Nsp9yBhTaRFht!Zsz^Rb}@ zj%(IjS95xP`b|Qs(oNL@Gw&1az_0n$@``(=Vdd8!5Jz}@<%yU zKxcN#jo$qPqmurLo9xaiqtD-%!JBUUR_hpGVT!d`Xx;*w<+l zBe%l27GsNhOYUmp@n|JA|6tTm=*X$J(dYf1bj{8{8>n{f{{42tho&7D3iAz5V$NCk z-E+&6Tw<35>dejVdb^eQzSmSaM{k#9am(5y&obeJ`sw9eS!oyk=-=j1cRob>F1qwW0r9{22TI$1VXh`t}nVZmN^Ty<6 z$8(h&lO1;3rl}sR(seV&(X&>g_c|mv6{=RQWrg%!2|PS1(4dZ*M3-FW>(t}=)~-v+ zhR4!#Hk*@#n)3!mydL-Xx3qaUhLQ*mVLoqIJY@)t^SSNZ(}wZp9|JPnf?+&Q%+2XES13M%{R#&S#N zYHad=%wB~JMDXnUKGp@XJj8FitR>OMN+-5#U6#RV+zJ+;8@c$UEgv!dr(C2()XFH~ zFwi~6`{If-^79vWSQi`)c5$O~YtK&zdXC4%bhn^Gk{S=)m|u@FSZ=z(gLFeHCwug@ z8-HA5L(lS4J63cX*Bejj*_1mwlx3R^4rG9knK*exn@>=0& zcpf9O@1hUt5VhYuU+ZS!i9qv^z>&s)fVR}ct0j2{E6N#tDJ34sHLUz~+I4kyFV^{- zQ9bo>$o$Oc!oZHBuZ>KutIGEmzY7G`KXZBa&xW9`y_+{zhdjFf$1lPCj~9rtYkvjY zgpc
-

BC Government

+ + + BC Government Logo +
{children}
diff --git a/appointment-booking/src/index.css b/appointment-booking/src/index.css index 6a50fd086..060b78149 100644 --- a/appointment-booking/src/index.css +++ b/appointment-booking/src/index.css @@ -46,6 +46,12 @@ p { margin: 0 auto; } +.bc-logo { + height: 50px; + width: auto; + display: block; +} + .header-content h1 { color: white; margin: 0; From 97ad51022df622d98dbb90d226dc1db3c8d9dfac Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:44:24 -0700 Subject: [PATCH 54/81] dev-44: pivot boilerplate to SSR with vendor-neutral node server --- appointment-booking/index.html | 4 +- appointment-booking/package.json | 9 +- appointment-booking/server.js | 103 +++++++++++++++++++++++ appointment-booking/src/entry-client.tsx | 11 +++ appointment-booking/src/entry-server.tsx | 15 ++++ appointment-booking/src/main.tsx | 10 --- 6 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 appointment-booking/server.js create mode 100644 appointment-booking/src/entry-client.tsx create mode 100644 appointment-booking/src/entry-server.tsx delete mode 100644 appointment-booking/src/main.tsx diff --git a/appointment-booking/index.html b/appointment-booking/index.html index 239f82461..f3e7ef759 100644 --- a/appointment-booking/index.html +++ b/appointment-booking/index.html @@ -7,7 +7,7 @@ appointment-booking -
- +
+ diff --git a/appointment-booking/package.json b/appointment-booking/package.json index 31d6967a8..5e8296f28 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -4,13 +4,16 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", + "dev": "node server.js", + "build": "tsc -b && npm run build:client && npm run build:server", + "build:client": "vite build --outDir dist/client", + "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server", "lint": "eslint .", "format": "prettier --write src", "format:check": "prettier --check src", + "start": "NODE_ENV=production node server.js", "type-check": "tsc --noEmit", - "preview": "vite preview", + "preview": "npm run start", "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0\"" }, "dependencies": { diff --git a/appointment-booking/server.js b/appointment-booking/server.js new file mode 100644 index 000000000..55fa80af8 --- /dev/null +++ b/appointment-booking/server.js @@ -0,0 +1,103 @@ +import fs from 'node:fs/promises' +import http from 'node:http' +import path from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const isProd = process.env.NODE_ENV === 'production' +const port = Number(process.env.PORT || 5173) + +const mimeTypes = { + '.css': 'text/css; charset=utf-8', + '.ico': 'image/x-icon', + '.js': 'text/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.png': 'image/png', + '.svg': 'image/svg+xml', + '.woff': 'font/woff', + '.woff2': 'font/woff2', +} + +let vite +if (!isProd) { + const { createServer } = await import('vite') + vite = await createServer({ + appType: 'custom', + root: __dirname, + server: { middlewareMode: true }, + }) +} + +const prodTemplate = isProd + ? await fs.readFile(path.resolve(__dirname, 'dist/client/index.html'), 'utf-8') + : '' + +const distClientDir = path.resolve(__dirname, 'dist/client') + +function send(res, statusCode, body, headers = {}) { + res.writeHead(statusCode, headers) + res.end(body) +} + +async function tryServeStatic(urlPath, res) { + if (!isProd) { + return false + } + + const normalizedPath = decodeURIComponent(urlPath.split('?')[0]) + const requestedFile = normalizedPath === '/' ? '/index.html' : normalizedPath + const absolutePath = path.resolve(distClientDir, `.${requestedFile}`) + + if (!absolutePath.startsWith(distClientDir)) { + send(res, 403, 'Forbidden') + return true + } + + try { + const file = await fs.readFile(absolutePath) + const extension = path.extname(absolutePath) + const contentType = mimeTypes[extension] || 'application/octet-stream' + send(res, 200, file, { 'Content-Type': contentType }) + return true + } catch { + return false + } +} + +const server = http.createServer(async (req, res) => { + const url = req.url || '/' + + try { + const served = await tryServeStatic(url, res) + if (served) { + return + } + + let template + let render + + if (!isProd) { + template = await fs.readFile(path.resolve(__dirname, 'index.html'), 'utf-8') + template = await vite.transformIndexHtml(url, template) + render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render + } else { + template = prodTemplate + render = (await import(pathToFileURL(path.resolve(__dirname, 'dist/server/entry-server.js')).href)).render + } + + const { appHtml } = await render(url) + const html = template.replace('', appHtml) + + send(res, 200, html, { 'Content-Type': 'text/html; charset=utf-8' }) + } catch (error) { + if (!isProd && vite) { + vite.ssrFixStacktrace(error) + } + send(res, 500, 'Internal Server Error') + } +}) + +server.listen(port, () => { + // Keep startup logging concise for local dev and container logs. + console.log(`SSR server running on http://localhost:${port}`) +}) \ No newline at end of file diff --git a/appointment-booking/src/entry-client.tsx b/appointment-booking/src/entry-client.tsx new file mode 100644 index 000000000..6d54ab973 --- /dev/null +++ b/appointment-booking/src/entry-client.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react' +import { hydrateRoot } from 'react-dom/client' +import './index.css' +import App from './App' + +hydrateRoot( + document.getElementById('app')!, + + + , +) diff --git a/appointment-booking/src/entry-server.tsx b/appointment-booking/src/entry-server.tsx new file mode 100644 index 000000000..3d919428f --- /dev/null +++ b/appointment-booking/src/entry-server.tsx @@ -0,0 +1,15 @@ +import { StrictMode } from 'react' +import { renderToString } from 'react-dom/server' +import App from './App' + +export function render(url: string) { + void url + + const appHtml = renderToString( + + + , + ) + + return { appHtml } +} diff --git a/appointment-booking/src/main.tsx b/appointment-booking/src/main.tsx deleted file mode 100644 index bef5202a3..000000000 --- a/appointment-booking/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) From de5594133075432bcd403b0e6740498c19164345 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Wed, 13 May 2026 15:57:35 -0700 Subject: [PATCH 55/81] dev-44: fix header logo loading using public assets and SSR middleware --- appointment-booking/public/gov3_bc_logo.png | Bin 0 -> 3603 bytes .../public/gov3_bc_logo_mobile.png | Bin 0 -> 3521 bytes appointment-booking/server.js | 52 +++++++++++------- .../src/components/common/Layout.tsx | 6 +- 4 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 appointment-booking/public/gov3_bc_logo.png create mode 100644 appointment-booking/public/gov3_bc_logo_mobile.png diff --git a/appointment-booking/public/gov3_bc_logo.png b/appointment-booking/public/gov3_bc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..08a9f2168bd8130d4204e7d126998ae0813414e6 GIT binary patch literal 3603 zcmaJ@2T)Vn77a+RA{`Mz6GR{ZBuZ#fq&Mk^^g!q}vQw1kp``mdiiZ~pv0@1Hq$-*whrXYI4k%$>P0hWZ-xG+Z*O)O9rdb)5&Z%+w(gtvo}M6jn1kOzU}m4bcj9o?N!Utxa&gr}`#Bk-^-Ubn?vAnuekDa3`CvF;;OT_22M2q4c=^MF z75M+{3kUEY8p03$w*=*`z_0wLAlO3B5UlF$=LD9KfQdUwUXub}mz9u&L1m?`iGih{ zlGh>7>kvt4ai}C53WH0_g8v%)iZoz(KZG;fNKO5(V!%p)-vx#8fkPlcK|vBh(h}Z& zNQk7YtSkg71(A{x2NdG|Azmo^U~w;hfj<^!9%@ehj()B_C|7SU@E=cm2k!us0zW_G zPZ7vpRe<&&fA1(2uzGq$Q({)hWVqAttuA9Mel*skaP=j182MezS*ffTk0d?whgVLbx| zULXEqYB|ope?>#@0`s5}vw&i3tDsF-Ikrt05d3GSxHb`h!F56Y-e?w30@wo6GN|M( zy6g-C(b%^T&;Sm&PVqA_w)OHKrjh4tY3x^cCU%OdHbE7iVJmc*ZFt2_x8#2ob$jy3 zJ*M`I*{05|{`m;iW?@N%*03a(QE+bRTDouz=d(=D|>;jmrLMVNiGKaJX|VZ+aa z?~PmIzX$D&817GbA8z&Te%1sw5`M<)^vmOaM4p};5$02YlMsJpoE#H&2UYgQOtG!P z+qLY(U$20=$NO^!tL22bq@%r=eT+MCx!{6HaeOPEVJ#EuY`-eQ}04}RBK zp+C+nY@lDX3}cq|F|PRU_Xuzr8R6yLI3*JUSY%d32|d*kKp>GX0#>NA)<15 z@?(AH*c_&13NuQ;&3AV-9UdK*mlscreU>V zZ#9Sh%vz+{{c>($s%L{Nh`9ATO(cwTr0bg+VW;Ec8&8Db(DHdkaxC=?r*GWy*L>D=g_CLo*0`GKTg>2~xpM06 z7fkTwNeDk@ZF)tu5o|Z|eBV(wvcG!#{bQR)G9uEFd5y&CW2t_eI;vgPgwU#0F2 z7tQUDNRFqnyauCJxqg0ay|Gvdnrm?6#x%S+P-GXVF@Ai^_6VBYH8K0t+c&>uX1IsW z%qch?BNKJkOANnWB)3&xB(zdQbyvXbyTzfJkkokY2+T^(t%@(eUr)}=N{1O%R?gS< zY^s+V&)Tne8qzAr`KHJ3n+xR97f{qsC7Z>Ws6K?r z7@6Dmj1{6ysk5)KGs+U`X=oy#JukX+Xf6oRl58`k&^jCiR(2{I=`(%OWjW7zmHoLV z*-upZwaXYQs?V`x^CoYDvLl!m>({1{@l6?`s(Z2??S6SVTx%-*UM3@MFmNT4%qM#L z4u@~%by)Qxvs*VU zoDTMMx8zdp;_xWx?={nHZB=|8gXv4(v@uSKJ$l5Z7Nf_Hf#Z}(a=A-~nh3r?*iXB2 zfi)r)Z0+B1ZxS_XcvHvtg5S-+U@kYs157_uBQK0s^HW_NGF13FmfLKUuh!j`p-gZ5 zxqsH@i#mBXndeuEN1#=F|1><6Y*RbD8=N z&~}xqKPuYa!#}Jxc&hgo)RZo1%a_EkoU-zG-qCH7d8Eg!rZpOYgn`A9lw*{Jx>K0# z8!9#gGT&%7dK&M1HHoh1nkw;%3K*6!3pQ4uP8EN)+p^cqhiVARCnfSRF zXn-P&n_)G{S_LyIk3pHMs0zz^ipbp17B4ALSiWrci4{KJ>EgBKvQaz!=xe>G%EW7U z<$!>dAVS(BQ^c&d(E}u?d=h$6V^B~s`qVKqTYBMW#1gqNtjb=%S#c*58-f5WJDa>Q z&Oqbq639pME~vFr@Y_{LTQy2sQO7tZQSb_SO!8oG@Qd+mO^Z_dbv4rqE9#d}5DLUP z%#j>lV_Vv6;neTrq!Y>VLomAwC(OyQ9J>OFwYZA;oJM0Vd5NU+AWu_jAjOG!apN7Y zMyo<4d#GcJjclRuIHq(y#VK-zo(u2tZB5ROJ#Zi+gR4Z#{C3MJ(tm`94q0v%tJe4= z6#Pru;sL2{2ZPNb#Hq52G1-?|E>yfcgduaNkL;oa?)42DNf0F|#_#Pgm2FL@`$TMu zs$1B%Sw*fg`iyCGazBki97Q!|(7Gl(VeKRD%80>MwQp@9y zwnfF;@y>?VXJMJbQ>6@z=0&x;J&oxyD*KO}H$rfmyb0llUv_BjxM63X1dOIH;RU4f z8=k@meZ8~Mq9^jYUry)n>pBQZ3NkDs*=>^*Yv)6~l6Fh=tPw?mA$C(vADjq%{gA6TxR;ot5fdpOS;oXPn-Be7ZD}dN2mE=caXpzdm;Z&+eddt zNUV`-bKbF2l?ex|2PE6&TiiU7lLOP; zv*i}Er)YnWZWbPK*N!iD6KbJt(?h(qk`MUI3dmbxnp6zZP&a0mkX5SRrRJ91 zlJb=cIhEvCM?@L3Fry7rY(b=Nj$LcBT;rao#&)rku<7$#)3;xNuQ~)-s8`4^@l>a* zdYIPs-{YnK5@IB`?mT7u>lo63%FhDDtvB7ct8I<(>s^{xg_2%>6S_$^Z6!$5uO!CJ zK1fG>G{c_K8lN9<#;D9qt!{C7$g;yEhA7%C)jWxTuypp8CrwO)WsC0*paGmBn@I_b zi_#uUd2lITlDf@F?fx=y=QfQx5eK-!f`nJ$)q2`lZ+7DR=|b^x?J|2JK5+ zx`7VQpd~DsYr9v@tMri|nSCl&-Ppvkop*4Hez&0Cp0$V@)=d50f9zUStS41-{L(ri zv4R1n%rS;ukZ$dM*vZJi0z$&J-9t?(Dr2_CT-Z{#VEXK0)12SlArd96km!dG1EZ~m zo(lPL=^aq!(xz7?Dp9wHgtA;o?bAk9vi@YI>R^L7WeK!fhg;RkyDL=_`zKPglWxs< z8nhvfgq~fFX**a*`f@Yr0$nJkb22W>Rwz%4aDiDz;@4F^czj+Bi?+7DsbERhNEC$k z@f>5-3!Cp1#HifgiD3)rjv6_$jf*4E?4S2j1mqXY&~buq1s%RL87bFaqsZwRt0k?= zV(v{|fLzRZomy-sdE1*Wu}7RF`CY5h?ar9V6=snh-ig(^XdkKhzL2BE76a{EW0c*5LB5R5q%dA1D`IT$~LO@%-vujL>wO%8F&A5w4rb3#0torpJ8k2Fxa zK0xlC7#g-Sat}1fmsqTHw2NKnX~DJ6?d@3Nifn8BiA$rW25AT_MROdl^jx`^r2fk^ R;4cfLbxU8ZRK@PWzW^>bft&yU literal 0 HcmV?d00001 diff --git a/appointment-booking/public/gov3_bc_logo_mobile.png b/appointment-booking/public/gov3_bc_logo_mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..262b7e8ea268e35f38db80c49f26523d87d62f0e GIT binary patch literal 3521 zcmcgvX;c$g7EXX5tD-2YLOO`hts#|_P=X7D7L>3nAOB@ zf;0$mG?ClJYafCvIKkZO&c{^1a75)K#iWc)a7 z3;D9c@nkYFNFYQ+L|8?TtwfSw0@20>;UE!6Bs_w^OScJSP$XU`HT%NAfTe5+Uo7K` zgjfY56eJ3h+2IhSQ!WU^GqghKbeNEU5h5WmfoKINT$%)O*fThBm?U)4IEPJuLtz0d zlt~dRaRw{qiDV)vPxK$CXO{nB00}LVIb-9SS_Fa_6H=LDIO4{1K)#8VdT$fM1W#Bh z3X`y5$8bcZnZg?}=pcb18O%U#I~)lh0eFCdCs}(F=^&8?QiyoK8U%nTC{x7YbGLmB zrQ!h_Zv;mrfdKt4P$UC5kPP~-U=ADPiX;LE(a#q^!7xEA48~z+q5?XILPZk9Frtq9 z^}Mr#gS$k;<%c2*QqMK^SZ7BE8qtPE!;`FtlX96%&{-&zK|(g{%&@~DzFP759FPLj z=rF*g<0+7hHJ(D_1mWpKRhXogRhqv6Hy~LTp8jBawl^ zVGeG3lK+ip{4KlF`y+TTLOGQq-#qv!+for%76D1%)xk*QzSO?^mny)LkmA5vG9fl! zsHltYzo=vLAYm|!bR7bY@ISlr_YQYT=l|6mf}-6_9Tef;&v0_>tJZ{gKRJc;JH>FO z(IOu+tr`|0k&+;d*s!`^9fg{8$eFR)oA>Z#mVc=C`uTe-UN_9j*&oMKn`f`GAM=E% zl=vEB>cY@*ia#IANKxZq4;v*KF)i(}ixY^rlq-Q}wdbSr)C%}~HJoIro=U}iBX3@C z7e6?vyHwQJxtH9U8O{X@1R1}S+|INK9qg|8M|lvz~bJdX=km-zR@Z zvqSQSIY~LKz9a7?8ck2C?sNxnk5}3j-pCqf4Pkz;Fd6Xsyep+V#SOq9+Erfo>6A1~|kCTQfR^&z{v-{9PbgqxZcw=NFP@?WEGfl1d24Or#TFC3v;(eDQ%azDlA z4;r6Z;@q!ixFXxtwk&s^(uOnJ3ZJyBtr~iag+x&_N7P%(EH@#k`{`dT5-l_*998vT?miisOuyG*F zg;{$lUMVhArTMm}#mg9ys0?j<;T%z^OSxlw|L0zI;m2gm1(W8vPNF+6az4GW*f46? z2p`J(bUE3_;@4dI0$b{X8-kHJ$AhxSq+2I=lT6VVy;%t>9Fb?F}PHSCwOgfJ$;8z z?4_5CNy>^UUwD$0e_)R&x-3j^xbp{V>cgn=E9jbstoY%oBbVE0Yk0$`@KKP`MR~EE zeo`xNj`{B7n$nQtzuVu@9JrCDG}>m|P7kVk6!~FHPq%dZZljT_-zt@Y#+_@2^e@C# z5|-Ovn~?7uT~}W>Nsp9yBhTaRFht!Zsz^Rb}@ zj%(IjS95xP`b|Qs(oNL@Gw&1az_0n$@``(=Vdd8!5Jz}@<%yU zKxcN#jo$qPqmurLo9xaiqtD-%!JBUUR_hpGVT!d`Xx;*w<+l zBe%l27GsNhOYUmp@n|JA|6tTm=*X$J(dYf1bj{8{8>n{f{{42tho&7D3iAz5V$NCk z-E+&6Tw<35>dejVdb^eQzSmSaM{k#9am(5y&obeJ`sw9eS!oyk=-=j1cRob>F1qwW0r9{22TI$1VXh`t}nVZmN^Ty<6 z$8(h&lO1;3rl}sR(seV&(X&>g_c|mv6{=RQWrg%!2|PS1(4dZ*M3-FW>(t}=)~-v+ zhR4!#Hk*@#n)3!mydL-Xx3qaUhLQ*mVLoqIJY@)t^SSNZ(}wZp9|JPnf?+&Q%+2XES13M%{R#&S#N zYHad=%wB~JMDXnUKGp@XJj8FitR>OMN+-5#U6#RV+zJ+;8@c$UEgv!dr(C2()XFH~ zFwi~6`{If-^79vWSQi`)c5$O~YtK&zdXC4%bhn^Gk{S=)m|u@FSZ=z(gLFeHCwug@ z8-HA5L(lS4J63cX*Bejj*_1mwlx3R^4rG9knK*exn@>=0& zcpf9O@1hUt5VhYuU+ZS!i9qv^z>&s)fVR}ct0j2{E6N#tDJ34sHLUz~+I4kyFV^{- zQ9bo>$o$Oc!oZHBuZ>KutIGEmzY7G`KXZBa&xW9`y_+{zhdjFf$1lPCj~9rtYkvjY zgpc', appHtml) + + send(res, 200, html, { 'Content-Type': 'text/html; charset=utf-8' }) +} + async function tryServeStatic(urlPath, res) { if (!isProd) { return false @@ -67,32 +86,25 @@ async function tryServeStatic(urlPath, res) { const server = http.createServer(async (req, res) => { const url = req.url || '/' + if (!isProd && vite) { + return vite.middlewares(req, res, async () => { + try { + await renderPage(url, res) + } catch (error) { + vite.ssrFixStacktrace(error) + send(res, 500, 'Internal Server Error') + } + }) + } + try { const served = await tryServeStatic(url, res) if (served) { return } - let template - let render - - if (!isProd) { - template = await fs.readFile(path.resolve(__dirname, 'index.html'), 'utf-8') - template = await vite.transformIndexHtml(url, template) - render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render - } else { - template = prodTemplate - render = (await import(pathToFileURL(path.resolve(__dirname, 'dist/server/entry-server.js')).href)).render - } - - const { appHtml } = await render(url) - const html = template.replace('', appHtml) - - send(res, 200, html, { 'Content-Type': 'text/html; charset=utf-8' }) - } catch (error) { - if (!isProd && vite) { - vite.ssrFixStacktrace(error) - } + await renderPage(url, res) + } catch { send(res, 500, 'Internal Server Error') } }) diff --git a/appointment-booking/src/components/common/Layout.tsx b/appointment-booking/src/components/common/Layout.tsx index 785b292cd..6f8886c36 100644 --- a/appointment-booking/src/components/common/Layout.tsx +++ b/appointment-booking/src/components/common/Layout.tsx @@ -1,6 +1,4 @@ import React from 'react' -import govLogo from '@/assets/img/gov3_bc_logo.png' -import govLogoMobile from '@/assets/img/gov3_bc_logo_mobile.png' interface LayoutProps { children: React.ReactNode @@ -12,8 +10,8 @@ export function Layout({ children }: LayoutProps) {
- - BC Government Logo + + BC Government Logo
From 69fb26dbc17b0d204f3cca6eb6be50c4b032c1c3 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Thu, 14 May 2026 16:26:38 -0700 Subject: [PATCH 56/81] dev-44: subtask 3 - add runtime config loader, API client, and booking API groundwork --- appointment-booking/server.js | 73 ++++++++++++++++++ appointment-booking/src/App.tsx | 37 ++++++++- .../src/models/runtime-config.ts | 4 + .../src/services/api-client.service.ts | 77 +++++++++++++++++++ .../src/services/booking-api.service.ts | 5 ++ .../src/services/runtime-config.service.ts | 30 ++++++++ 6 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 appointment-booking/src/models/runtime-config.ts create mode 100644 appointment-booking/src/services/api-client.service.ts create mode 100644 appointment-booking/src/services/booking-api.service.ts create mode 100644 appointment-booking/src/services/runtime-config.service.ts diff --git a/appointment-booking/server.js b/appointment-booking/server.js index c8966bf56..06538fc24 100644 --- a/appointment-booking/server.js +++ b/appointment-booking/server.js @@ -6,6 +6,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const isProd = process.env.NODE_ENV === 'production' const port = Number(process.env.PORT || 5173) +const apiProxyTarget = (process.env.API_PROXY_TARGET || 'http://localhost:5000').replace(/\/$/, '') const mimeTypes = { '.css': 'text/css; charset=utf-8', @@ -39,6 +40,60 @@ function send(res, statusCode, body, headers = {}) { res.end(body) } +function getRequestBody(req) { + return new Promise((resolve, reject) => { + const chunks = [] + req.on('data', (chunk) => chunks.push(chunk)) + req.on('end', () => { + if (!chunks.length) { + resolve(undefined) + return + } + + resolve(Buffer.concat(chunks)) + }) + req.on('error', reject) + }) +} + +async function tryProxyApiRequest(req, res, urlPath) { + if (!urlPath.startsWith('/api/v1')) { + return false + } + + const targetUrl = `${apiProxyTarget}${urlPath}` + const requestBody = await getRequestBody(req) + + try { + const proxyResponse = await fetch(targetUrl, { + method: req.method, + headers: { + ...(req.headers || {}), + host: undefined, + }, + body: requestBody, + }) + + const responseBuffer = Buffer.from(await proxyResponse.arrayBuffer()) + send(res, proxyResponse.status, responseBuffer, { + 'Content-Type': proxyResponse.headers.get('content-type') || 'application/json; charset=utf-8', + }) + return true + } catch { + send(res, 502, JSON.stringify({ message: 'Unable to reach booking API' }), { + 'Content-Type': 'application/json; charset=utf-8', + }) + return true + } +} + +function getRuntimeConfig() { + return { + apiBaseUrl: process.env.API_BASE_URL || process.env.VITE_API_BASE_URL || '/api/v1', + requestTimeoutMs: Number(process.env.REQUEST_TIMEOUT_MS || 10000), + } +} + async function renderPage(url, res) { let template let render @@ -86,6 +141,24 @@ async function tryServeStatic(urlPath, res) { const server = http.createServer(async (req, res) => { const url = req.url || '/' + try { + const proxied = await tryProxyApiRequest(req, res, url) + if (proxied) { + return + } + } catch { + send(res, 500, 'Internal Server Error') + return + } + + if (url.startsWith('/config/runtime-config.json')) { + send(res, 200, JSON.stringify(getRuntimeConfig()), { + 'Cache-Control': 'no-store', + 'Content-Type': 'application/json; charset=utf-8', + }) + return + } + if (!isProd && vite) { return vite.middlewares(req, res, async () => { try { diff --git a/appointment-booking/src/App.tsx b/appointment-booking/src/App.tsx index 0eae59980..df7595a53 100644 --- a/appointment-booking/src/App.tsx +++ b/appointment-booking/src/App.tsx @@ -1,11 +1,46 @@ +import { useEffect, useState } from 'react' import { Layout, Page } from '@/components/common' +import { ApiClient, ApiClientError } from '@/services/api-client.service' +import { getOffices } from '@/services/booking-api.service' +import { loadRuntimeConfig } from '@/services/runtime-config.service' +import type { RuntimeConfig } from '@/models/runtime-config' import './App.css' function App() { + const [config, setConfig] = useState(null) + const [configError, setConfigError] = useState(null) + const [apiStatus, setApiStatus] = useState('Loading app settings...') + + useEffect(() => { + async function bootstrap() { + try { + const runtimeConfig = await loadRuntimeConfig() + setConfig(runtimeConfig) + setConfigError(null) + + const apiClient = new ApiClient(runtimeConfig) + await getOffices(apiClient) + setApiStatus('Booking API connection established.') + } catch (error) { + if (error instanceof ApiClientError) { + setApiStatus(`Booking API unavailable: ${error.message}`) + } else { + setConfigError('Failed to initialize application settings.') + setApiStatus('Booking API connection not attempted due to config error.') + } + } + } + + void bootstrap() + }, []) + return ( -

React boilerplate with BC Gov Design System ready for development

+

Runtime settings and booking API groundwork are active.

+

{config ? `API base URL: ${config.apiBaseUrl}` : 'API base URL: loading...'}

+

{apiStatus}

+ {configError ?

{configError}

: null}
) diff --git a/appointment-booking/src/models/runtime-config.ts b/appointment-booking/src/models/runtime-config.ts new file mode 100644 index 000000000..6f34c28a5 --- /dev/null +++ b/appointment-booking/src/models/runtime-config.ts @@ -0,0 +1,4 @@ +export interface RuntimeConfig { + apiBaseUrl: string + requestTimeoutMs: number +} diff --git a/appointment-booking/src/services/api-client.service.ts b/appointment-booking/src/services/api-client.service.ts new file mode 100644 index 000000000..a385a844d --- /dev/null +++ b/appointment-booking/src/services/api-client.service.ts @@ -0,0 +1,77 @@ +import type { RuntimeConfig } from '@/models/runtime-config' + +interface RequestOptions extends Omit { + body?: unknown + timeoutMs?: number +} + +export class ApiClientError extends Error { + status: number + details?: unknown + + constructor(message: string, status = 0, details?: unknown) { + super(message) + this.name = 'ApiClientError' + this.status = status + this.details = details + } +} + +export class ApiClient { + private baseUrl: string + private timeoutMs: number + + constructor(config: RuntimeConfig) { + this.baseUrl = config.apiBaseUrl.replace(/\/$/, '') + this.timeoutMs = config.requestTimeoutMs + } + + async get(path: string, options: Omit = {}): Promise { + return this.request(path, { ...options, method: 'GET' }) + } + + private async request(path: string, options: RequestOptions): Promise { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), options.timeoutMs || this.timeoutMs) + + const normalizedPath = path.startsWith('/') ? path : `/${path}` + const url = `${this.baseUrl}${normalizedPath}` + + try { + const response = await fetch(url, { + ...options, + body: options.body ? JSON.stringify(options.body) : undefined, + headers: { + 'Content-Type': 'application/json', + ...(options.headers || {}), + }, + signal: controller.signal, + }) + + if (!response.ok) { + let details: unknown + try { + details = await response.json() + } catch { + details = await response.text() + } + + throw new ApiClientError(`Request failed (${response.status})`, response.status, details) + } + + return (await response.json()) as T + } catch (error) { + if (error instanceof ApiClientError) { + throw error + } + + if (error instanceof Error && error.name === 'AbortError') { + throw new ApiClientError('Request timed out') + } + + throw new ApiClientError('Network error') + } finally { + clearTimeout(timeout) + } + } +} diff --git a/appointment-booking/src/services/booking-api.service.ts b/appointment-booking/src/services/booking-api.service.ts new file mode 100644 index 000000000..8ab9b44a1 --- /dev/null +++ b/appointment-booking/src/services/booking-api.service.ts @@ -0,0 +1,5 @@ +import { ApiClient } from '@/services/api-client.service' + +export async function getOffices(client: ApiClient) { + return client.get('/offices/') +} diff --git a/appointment-booking/src/services/runtime-config.service.ts b/appointment-booking/src/services/runtime-config.service.ts new file mode 100644 index 000000000..ff3ddff51 --- /dev/null +++ b/appointment-booking/src/services/runtime-config.service.ts @@ -0,0 +1,30 @@ +import type { RuntimeConfig } from '@/models/runtime-config' + +const DEFAULT_TIMEOUT_MS = 10000 +const RUNTIME_CONFIG_URL = '/config/runtime-config.json' + +function fromEnvironment(): RuntimeConfig { + return { + apiBaseUrl: import.meta.env.VITE_API_BASE_URL || '/api/v1', + requestTimeoutMs: Number(import.meta.env.VITE_REQUEST_TIMEOUT_MS || DEFAULT_TIMEOUT_MS), + } +} + +export async function loadRuntimeConfig(): Promise { + try { + const response = await fetch(RUNTIME_CONFIG_URL, { cache: 'no-store' }) + if (!response.ok) { + return fromEnvironment() + } + + const json = (await response.json()) as Partial + const fallback = fromEnvironment() + + return { + apiBaseUrl: json.apiBaseUrl || fallback.apiBaseUrl, + requestTimeoutMs: Number(json.requestTimeoutMs || fallback.requestTimeoutMs), + } + } catch { + return fromEnvironment() + } +} From 879c1e5f58436c072239a9d7697da06ec92373cf Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Fri, 15 May 2026 14:49:59 -0700 Subject: [PATCH 57/81] dev-44: subtask 4 - add automated tests and GitHub Actions CI for appointment-booking --- .github/workflows/appointment-booking-ci.yaml | 34 + appointment-booking/package-lock.json | 1127 ++++++++++++++++- appointment-booking/package.json | 12 +- appointment-booking/src/App.test.tsx | 48 + .../src/services/api-client.service.test.ts | 60 + appointment-booking/src/test/setup.ts | 7 + appointment-booking/vite.config.ts | 7 +- 7 files changed, 1290 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/appointment-booking-ci.yaml create mode 100644 appointment-booking/src/App.test.tsx create mode 100644 appointment-booking/src/services/api-client.service.test.ts create mode 100644 appointment-booking/src/test/setup.ts diff --git a/.github/workflows/appointment-booking-ci.yaml b/.github/workflows/appointment-booking-ci.yaml new file mode 100644 index 000000000..9e7b77566 --- /dev/null +++ b/.github/workflows/appointment-booking-ci.yaml @@ -0,0 +1,34 @@ +name: Appointment Booking CI + +on: + workflow_dispatch: + pull_request: + paths: + - 'appointment-booking/**' + - '.github/workflows/appointment-booking-ci.yaml' + +jobs: + quality-checks: + name: Quality Checks + runs-on: ubuntu-latest + + defaults: + run: + working-directory: appointment-booking + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: appointment-booking/package-lock.json + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Run quality gates + run: npm run ci:check diff --git a/appointment-booking/package-lock.json b/appointment-booking/package-lock.json index 6e3094034..20eb76452 100644 --- a/appointment-booking/package-lock.json +++ b/appointment-booking/package-lock.json @@ -15,6 +15,9 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/node": "^24.12.3", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -28,13 +31,22 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "jsdom": "^29.1.1", "license-checker": "^25.0.1", "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", - "vite": "^8.0.12" + "vite": "^8.0.12", + "vitest": "^4.1.6" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, "node_modules/@adobe/react-spectrum": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.47.0.tgz", @@ -97,6 +109,57 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -376,6 +439,159 @@ "integrity": "sha512-kcen18Q/snP6M6JQ6XUQTlWXM1Gnm6y8Zk0i29K+T/Y0m/ab4PufmGpl43l/EJKCnYgpOaZFSXDK7CCO6P0INw==", "license": "Apache-2.0" }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", + "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", + "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -538,6 +754,24 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", @@ -1666,6 +1900,13 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", @@ -1675,6 +1916,91 @@ "tslib": "^2.8.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -1686,6 +2012,31 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -2006,6 +2357,119 @@ } } }, + "node_modules/@vitest/expect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", + "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", + "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", + "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", + "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", + "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", + "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", + "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2053,6 +2517,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -2078,6 +2552,16 @@ "node": ">=10" } }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -2233,6 +2717,16 @@ "dev": true, "license": "MIT" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -2282,6 +2776,16 @@ "node": ">=6.0.0" } }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/brace-expansion": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", @@ -2400,6 +2904,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2486,12 +3000,47 @@ "node": ">= 8" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2624,6 +3173,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2658,6 +3217,13 @@ "node": ">=0.10.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2690,6 +3256,19 @@ "dev": true, "license": "ISC" }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", @@ -2807,6 +3386,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -3173,6 +3759,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3183,6 +3779,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3670,6 +4276,19 @@ "dev": true, "license": "ISC" }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3687,7 +4306,17 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/inflight": { @@ -3973,6 +4602,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4156,6 +4792,57 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -4591,6 +5278,26 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4601,6 +5308,23 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -4872,6 +5596,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4982,6 +5717,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5019,6 +5767,13 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5117,6 +5872,41 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5317,6 +6107,20 @@ "once": "^1.3.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5361,6 +6165,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "2.0.0-next.6", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", @@ -5481,6 +6295,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -5645,6 +6472,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/slide": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", @@ -5732,6 +6566,20 @@ "spdx-ranges": "^2.0.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -5844,6 +6692,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -5870,6 +6731,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -5886,6 +6754,23 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -5903,6 +6788,62 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.30" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/treeify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", @@ -6080,6 +7021,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -6233,6 +7184,144 @@ } } }, + "node_modules/vitest": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", + "integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.6", + "@vitest/mocker": "4.1.6", + "@vitest/pretty-format": "4.1.6", + "@vitest/runner": "4.1.6", + "@vitest/snapshot": "4.1.6", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.6", + "@vitest/browser-preview": "4.1.6", + "@vitest/browser-webdriverio": "4.1.6", + "@vitest/coverage-istanbul": "4.1.6", + "@vitest/coverage-v8": "4.1.6", + "@vitest/ui": "4.1.6", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6338,6 +7427,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -6355,6 +7461,23 @@ "dev": true, "license": "ISC" }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/appointment-booking/package.json b/appointment-booking/package.json index 5e8296f28..c88db8d0b 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -8,13 +8,16 @@ "build": "tsc -b && npm run build:client && npm run build:server", "build:client": "vite build --outDir dist/client", "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server", + "test": "vitest run", + "test:watch": "vitest", "lint": "eslint .", "format": "prettier --write src", "format:check": "prettier --check src", "start": "NODE_ENV=production node server.js", "type-check": "tsc --noEmit", "preview": "npm run start", - "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0\"" + "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0\"", + "ci:check": "npm run lint && npm run type-check && npm run test && npm run build && npm run license-check" }, "dependencies": { "@bcgov/design-system-react-components": "^0.7.0", @@ -24,6 +27,9 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/node": "^24.12.3", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -37,10 +43,12 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "jsdom": "^29.1.1", "license-checker": "^25.0.1", "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", - "vite": "^8.0.12" + "vite": "^8.0.12", + "vitest": "^4.1.6" } } diff --git a/appointment-booking/src/App.test.tsx b/appointment-booking/src/App.test.tsx new file mode 100644 index 000000000..542d61e67 --- /dev/null +++ b/appointment-booking/src/App.test.tsx @@ -0,0 +1,48 @@ +import { render, screen, waitFor } from '@testing-library/react' +import type { ReactNode } from 'react' +import { describe, expect, it, vi, beforeEach } from 'vitest' +import App from './App' + +vi.mock('@/components/common', () => ({ + Layout: ({ children }: { children: ReactNode }) =>
{children}
, + Page: ({ children, title }: { children: ReactNode; title: string }) => ( +
+

{title}

+ {children} +
+ ), +})) + +const loadRuntimeConfigMock = vi.fn() +const getOfficesMock = vi.fn() + +vi.mock('@/services/runtime-config.service', () => ({ + loadRuntimeConfig: () => loadRuntimeConfigMock(), +})) + +vi.mock('@/services/booking-api.service', () => ({ + getOffices: () => getOfficesMock(), +})) + +describe('App', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('shows successful API bootstrap state', async () => { + loadRuntimeConfigMock.mockResolvedValue({ + apiBaseUrl: '/api/v1', + requestTimeoutMs: 10000, + }) + getOfficesMock.mockResolvedValue({ offices: [], errors: {} }) + + render() + + expect(screen.getByRole('heading', { name: 'Appointment Booking' })).toBeInTheDocument() + + await waitFor(() => { + expect(screen.getByText('API base URL: /api/v1')).toBeInTheDocument() + expect(screen.getByText('Booking API connection established.')).toBeInTheDocument() + }) + }) +}) diff --git a/appointment-booking/src/services/api-client.service.test.ts b/appointment-booking/src/services/api-client.service.test.ts new file mode 100644 index 000000000..28daa992f --- /dev/null +++ b/appointment-booking/src/services/api-client.service.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, vi, afterEach } from 'vitest' +import { ApiClient } from './api-client.service' + +const config = { + apiBaseUrl: '/api/v1', + requestTimeoutMs: 50, +} + +describe('ApiClient', () => { + afterEach(() => { + vi.restoreAllMocks() + vi.useRealTimers() + }) + + it('throws ApiClientError with status for non-2xx responses', async () => { + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue( + new Response(JSON.stringify({ message: 'broken' }), { + status: 500, + headers: { + 'Content-Type': 'application/json', + }, + }), + ) + + const apiClient = new ApiClient(config) + + await expect(apiClient.get('/offices/')).rejects.toMatchObject({ + name: 'ApiClientError', + status: 500, + }) + + expect(fetchSpy).toHaveBeenCalledOnce() + }) + + it('throws timeout error when request exceeds configured timeout', async () => { + vi.useFakeTimers() + + vi.spyOn(globalThis, 'fetch').mockImplementation( + (_, init?: RequestInit): Promise => + new Promise((_, reject) => { + init?.signal?.addEventListener('abort', () => { + const abortError = new Error('Aborted') + abortError.name = 'AbortError' + reject(abortError) + }) + }), + ) + + const apiClient = new ApiClient({ ...config, requestTimeoutMs: 10 }) + const request = apiClient.get('/offices/') + const rejection = expect(request).rejects.toMatchObject({ + name: 'ApiClientError', + message: 'Request timed out', + status: 0, + }) + + await vi.advanceTimersByTimeAsync(11) + await rejection + }) +}) diff --git a/appointment-booking/src/test/setup.ts b/appointment-booking/src/test/setup.ts new file mode 100644 index 000000000..2287bd588 --- /dev/null +++ b/appointment-booking/src/test/setup.ts @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom/vitest' +import { afterEach } from 'vitest' +import { cleanup } from '@testing-library/react' + +afterEach(() => { + cleanup() +}) diff --git a/appointment-booking/vite.config.ts b/appointment-booking/vite.config.ts index ace547ded..12395e650 100644 --- a/appointment-booking/vite.config.ts +++ b/appointment-booking/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' +import { defineConfig } from 'vitest/config' // https://vite.dev/config/ export default defineConfig({ @@ -10,4 +10,9 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + test: { + environment: 'jsdom', + setupFiles: './src/test/setup.ts', + include: ['src/**/*.test.ts', 'src/**/*.test.tsx'], + }, }) From e21c5164a87cb8c09b32b9c4feef3a80e1732282 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Fri, 15 May 2026 15:45:24 -0700 Subject: [PATCH 58/81] dev-44: refine local startup flow and docs for appointment-booking --- appointment-booking/README.md | 234 +++++++++++++++++++++++-------- appointment-booking/package.json | 1 + appointment-booking/server.js | 33 ++++- 3 files changed, 203 insertions(+), 65 deletions(-) diff --git a/appointment-booking/README.md b/appointment-booking/README.md index 7dbf7ebf3..829da4bad 100644 --- a/appointment-booking/README.md +++ b/appointment-booking/README.md @@ -1,73 +1,187 @@ -# React + TypeScript + Vite +# Appointment Booking App -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +A modern React 19 + TypeScript appointment booking interface built for BC Gov, featuring server-side rendering (SSR), automated tests, and OpenShift deployment readiness. -Currently, two official plugins are available: +## Quick Start (clean machine) -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) +### Prerequisites +- Node.js 22+ +- npm 10+ +- Docker (for local Postgres) +- `uv` (for running Python backend) -## React Compiler +### 1. Clone and install frontend dependencies -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). +Run from repository root: -## Expanding the ESLint configuration +```bash +git clone https://github.com/bcgov/queue-management.git +cd queue-management/appointment-booking +npm install --legacy-peer-deps +``` + +### 2. Start database + +From repository root: + +```bash +cd ../ +docker start citz-sbc-queue-postgres-1 || docker compose up -d db +``` + +### 3. Start backend API first (required for API calls) + +In terminal A, from repository root: + +```bash +cd api +uv sync +DATABASE_HOST=127.0.0.1 \ +DATABASE_PORT=5432 \ +DATABASE_NAME=sbc_queue \ +DATABASE_USERNAME=postgres \ +DATABASE_PASSWORD=postgres \ +uv run gunicorn wsgi --bind=0.0.0.0:5100 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 +``` + +### 4. Start frontend + +In terminal B, from repository root: + +```bash +cd appointment-booking +npm run dev:local +``` + +Open: +- `http://localhost:5173` +- If 5173 is in use, the server automatically falls back to 5174. + +### 5. Verify everything is connected + +From repository root: + +```bash +curl -i http://localhost:5173/config/runtime-config.json +curl -i http://localhost:5173/api/v1/healthz/ +curl -i http://localhost:5173/api/v1/offices/ +``` + +Expected: all three return `HTTP/1.1 200 OK`. + +### Frontend-only checks (without backend) + +If you only want to verify SSR/frontend startup, backend is not required. +In that case, `api/v1/*` calls will fail until backend is running. + +### Run Checks + +```bash +# Run everything at once (lint + type-check + test + build + license) +npm run ci:check + +# Optional: run checks individually +npm run test +npm run test:watch +npm run lint +npm run type-check +npm run license-check +``` + +### Build for Production + +```bash +# Compile TypeScript, build client & server +npm run build -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: +# Output in ./dist/ +# - dist/client/ (browser assets) +# - dist/server/ (SSR entry point) -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... +# Run production build locally +NODE_ENV=production node server.js +# Opens at http://localhost:5173 +``` + +--- - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, +## Docker - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) +### Build Image + +```bash +docker build -t appointment-booking:latest . ``` -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) +### Run Container + +```bash +docker run -p 5173:5173 \ + -e API_PROXY_TARGET=http://host.docker.internal:5100 \ + -e NODE_ENV=production \ + appointment-booking:latest +``` + +**Note:** Use `host.docker.internal` on macOS/Windows to access localhost from container. On Linux, use the Docker network or actual IP. + +### Run with Docker Compose + +```bash +docker-compose up appointment-booking ``` + +(Assumes compose.yaml exists with service definition) + +--- + +## Environment Variables + +| Variable | Default | Purpose | +|----------|---------|---------| +| `API_PROXY_TARGET` | `http://localhost:5000` | Backend API URL for proxy (dev) or sidecar (prod) | +| `NODE_ENV` | `development` | Node environment (development/production) | +| `PORT` | `5173` | Server listen port | +| `FALLBACK_PORT` | `5174` | Backup port used when `PORT` is already in use | +| `REQUEST_TIMEOUT_MS` | `10000` | API request timeout in milliseconds | + +### Runtime Config + +The app loads configuration from `/config/runtime-config.json` endpoint (served by `server.js`): + +```json +{ + "apiBaseUrl": "/api/v1", + "requestTimeoutMs": 10000 +} +``` + +This endpoint reads from environment variables at startup, allowing Kubernetes ConfigMaps/Secrets to drive configuration without rebuilding the image. + +--- + +## License + +MIT + Apache-2.0 (for BC Gov design system components) + +See `LICENSE` file and run `npm run license-check` to verify all dependencies comply. + +--- + +## Contributing + +1. Create a feature branch +2. Make changes and run tests: `npm run test` +3. Lint and format: `npm run lint && npm run format` +4. Push and open a PR +5. GitHub Actions runs quality checks automatically +6. Merge once checks pass + +--- + +## Links + +- **BC Gov Design System:** https://github.com/bcgov/design-system +- **React Docs:** https://react.dev +- **Vite Docs:** https://vite.dev +- **TypeScript Docs:** https://www.typescriptlang.org +- **OpenShift Docs:** https://docs.openshift.com diff --git a/appointment-booking/package.json b/appointment-booking/package.json index c88db8d0b..c6429912a 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "node server.js", + "dev:local": "API_PROXY_TARGET=http://localhost:5100 node server.js", "build": "tsc -b && npm run build:client && npm run build:server", "build:client": "vite build --outDir dist/client", "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server", diff --git a/appointment-booking/server.js b/appointment-booking/server.js index 06538fc24..f476d08eb 100644 --- a/appointment-booking/server.js +++ b/appointment-booking/server.js @@ -5,7 +5,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const isProd = process.env.NODE_ENV === 'production' -const port = Number(process.env.PORT || 5173) +const preferredPort = Number(process.env.PORT || 5173) +const fallbackPort = Number(process.env.FALLBACK_PORT || preferredPort + 1) const apiProxyTarget = (process.env.API_PROXY_TARGET || 'http://localhost:5000').replace(/\/$/, '') const mimeTypes = { @@ -182,7 +183,29 @@ const server = http.createServer(async (req, res) => { } }) -server.listen(port, () => { - // Keep startup logging concise for local dev and container logs. - console.log(`SSR server running on http://localhost:${port}`) -}) \ No newline at end of file +let isRetryingWithFallbackPort = false + +function startServer(onPort) { + server.listen(onPort, () => { + // Keep startup logging concise for local dev and container logs. + console.log(`SSR server running on http://localhost:${onPort}`) + }) +} + +server.on('error', (error) => { + if ( + error && + error.code === 'EADDRINUSE' && + !isRetryingWithFallbackPort && + preferredPort !== fallbackPort + ) { + isRetryingWithFallbackPort = true + console.warn(`Port ${preferredPort} is already in use. Falling back to ${fallbackPort}.`) + startServer(fallbackPort) + return + } + + throw error +}) + +startServer(preferredPort) \ No newline at end of file From 867c044b2a16647099938cb915bcceffa37b2ba2 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Fri, 15 May 2026 15:47:31 -0700 Subject: [PATCH 59/81] Add Dockerfile for appointment-booking SSR app Introduce a multi-stage Dockerfile for the appointment-booking SSR application. The builder stage uses node:22-alpine to install dependencies (npm ci --legacy-peer-deps), copy source, and run the build. The runtime stage installs production dependencies, copies built artifacts (dist, server.js, public) from the builder, exposes port 5173, configures a healthcheck against /config/runtime-config.json, sets NODE_ENV=production, and runs server.js. --- appointment-booking/Dockerfile | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 appointment-booking/Dockerfile diff --git a/appointment-booking/Dockerfile b/appointment-booking/Dockerfile new file mode 100644 index 000000000..d165d9357 --- /dev/null +++ b/appointment-booking/Dockerfile @@ -0,0 +1,42 @@ +# Multi-stage build for appointment-booking SSR app +# Stage 1: Builder +FROM node:22-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci --legacy-peer-deps + +# Copy source +COPY . . + +# Build client and server +RUN npm run build + +# Stage 2: Runtime +FROM node:22-alpine + +WORKDIR /app + +# Install only production dependencies +COPY package*.json ./ +RUN npm ci --omit=dev --legacy-peer-deps + +# Copy built artifacts from builder +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/server.js ./server.js +COPY --from=builder /app/public ./public + +# Health check +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD node -e "require('http').get('http://localhost:5173/config/runtime-config.json', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" || exit 1 + +# Expose port +EXPOSE 5173 + +# Run production server +ENV NODE_ENV=production +CMD ["node", "server.js"] From 6aa79c508467f2f250a3404604031cb14a0ae358 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Tue, 19 May 2026 09:19:00 -0700 Subject: [PATCH 60/81] Add npm install command to README --- appointment-booking/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/appointment-booking/README.md b/appointment-booking/README.md index 829da4bad..42c258717 100644 --- a/appointment-booking/README.md +++ b/appointment-booking/README.md @@ -50,6 +50,7 @@ In terminal B, from repository root: ```bash cd appointment-booking +npm install --legacy-peer-deps npm run dev:local ``` From 98e5ec76c8cd59c52769c588d51b903a8706b6a0 Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Tue, 19 May 2026 09:21:01 -0700 Subject: [PATCH 61/81] Simplify setup instructions in README Removed unnecessary commands from the setup instructions. --- appointment-booking/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/appointment-booking/README.md b/appointment-booking/README.md index 42c258717..3c4885d3d 100644 --- a/appointment-booking/README.md +++ b/appointment-booking/README.md @@ -16,8 +16,6 @@ Run from repository root: ```bash git clone https://github.com/bcgov/queue-management.git -cd queue-management/appointment-booking -npm install --legacy-peer-deps ``` ### 2. Start database From 6f177ae5493ff0d513be2e30d91253f4414d5f6e Mon Sep 17 00:00:00 2001 From: Veenu Punyani Date: Tue, 19 May 2026 16:14:07 -0700 Subject: [PATCH 62/81] Cleanup appointment-booking setup & docs Remove legacy npm flags and tidy dev setup for the appointment-booking app. Updated CI workflow and Dockerfile to use plain `npm ci`, added a VS Code launch configuration for the SSR front end, and added a recorded CI output file. README was simplified and clarified (dev vs prod proxy vars and startup steps). package-lock.json was adjusted to reflect dependency/peer changes and an unused Button component was removed along with small frontend/config tweaks. also removed hardcoded css by importing the bc desing system css file --- .github/workflows/appointment-booking-ci.yaml | 2 +- .vscode/launch.json | 19 + appointment-booking/Dockerfile | 4 +- appointment-booking/README.md | 76 +- appointment-booking/ci_output.txt | 510 +++++ appointment-booking/package-lock.json | 2021 +---------------- appointment-booking/package.json | 3 +- appointment-booking/server.js | 69 - .../src/components/common/Button.tsx | 6 - .../src/components/common/Layout.tsx | 31 +- .../src/components/common/index.ts | 1 - appointment-booking/src/entry-client.tsx | 2 + appointment-booking/src/index.css | 57 +- appointment-booking/vite.config.ts | 10 + 14 files changed, 662 insertions(+), 2149 deletions(-) create mode 100644 appointment-booking/ci_output.txt delete mode 100644 appointment-booking/src/components/common/Button.tsx diff --git a/.github/workflows/appointment-booking-ci.yaml b/.github/workflows/appointment-booking-ci.yaml index 9e7b77566..dafd400a8 100644 --- a/.github/workflows/appointment-booking-ci.yaml +++ b/.github/workflows/appointment-booking-ci.yaml @@ -28,7 +28,7 @@ jobs: cache-dependency-path: appointment-booking/package-lock.json - name: Install dependencies - run: npm ci --legacy-peer-deps + run: npm ci - name: Run quality gates run: npm run ci:check diff --git a/.vscode/launch.json b/.vscode/launch.json index d865984a3..379618233 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,6 +20,7 @@ // Run the components used for the Queue Management system. "name": "Queue Management", "configurations": [ + "appointment_booking_front_end", "appointment_front_end", "queue_management_api", "queue_management_front_end", @@ -28,6 +29,24 @@ } ], "configurations": [ + { + // Run the React SSR front end for the new Appointment Booking application. + "cwd": "${workspaceFolder}/appointment-booking", + "env": { + "API_PROXY_TARGET": "http://localhost:5000" + }, + "name": "appointment_booking_front_end", + "request": "launch", + "runtimeArgs": [ + "run", + "dev" + ], + "runtimeExecutable": "npm", + "skipFiles": [ + "/**" + ], + "type": "node" + }, { // Run the Vue.js front end for the Appointment application. "cwd": "${workspaceFolder}/appointment-frontend", diff --git a/appointment-booking/Dockerfile b/appointment-booking/Dockerfile index d165d9357..4ae2c78f7 100644 --- a/appointment-booking/Dockerfile +++ b/appointment-booking/Dockerfile @@ -8,7 +8,7 @@ WORKDIR /app COPY package*.json ./ # Install dependencies -RUN npm ci --legacy-peer-deps +RUN npm ci # Copy source COPY . . @@ -23,7 +23,7 @@ WORKDIR /app # Install only production dependencies COPY package*.json ./ -RUN npm ci --omit=dev --legacy-peer-deps +RUN npm ci --omit=dev # Copy built artifacts from builder COPY --from=builder /app/dist ./dist diff --git a/appointment-booking/README.md b/appointment-booking/README.md index 3c4885d3d..9ae9e8f5e 100644 --- a/appointment-booking/README.md +++ b/appointment-booking/README.md @@ -1,77 +1,38 @@ # Appointment Booking App -A modern React 19 + TypeScript appointment booking interface built for BC Gov, featuring server-side rendering (SSR), automated tests, and OpenShift deployment readiness. +A React 19 + TypeScript appointment booking interface built for BC Gov, featuring server-side rendering (SSR), automated tests, and OpenShift deployment readiness. -## Quick Start (clean machine) +## Prerequisites -### Prerequisites - Node.js 22+ - npm 10+ -- Docker (for local Postgres) -- `uv` (for running Python backend) -### 1. Clone and install frontend dependencies +For database and backend setup, follow the instructions in the [root README](../README.md). -Run from repository root: +## Local Development -```bash -git clone https://github.com/bcgov/queue-management.git -``` - -### 2. Start database - -From repository root: +### Install dependencies ```bash -cd ../ -docker start citz-sbc-queue-postgres-1 || docker compose up -d db +cd appointment-booking +npm install ``` -### 3. Start backend API first (required for API calls) - -In terminal A, from repository root: +### Start the frontend ```bash -cd api -uv sync -DATABASE_HOST=127.0.0.1 \ -DATABASE_PORT=5432 \ -DATABASE_NAME=sbc_queue \ -DATABASE_USERNAME=postgres \ -DATABASE_PASSWORD=postgres \ -uv run gunicorn wsgi --bind=0.0.0.0:5100 --access-logfile=- --config=gunicorn_config.py --reload --timeout=0 -``` - -### 4. Start frontend - -In terminal B, from repository root: +# Default: proxies /api/v1 to http://localhost:5000 +npm run dev -```bash -cd appointment-booking -npm install --legacy-peer-deps +# If your local API runs on a different port (e.g. 5100 on macOS where port 5000 is reserved by mDNS): npm run dev:local ``` -Open: -- `http://localhost:5173` -- If 5173 is in use, the server automatically falls back to 5174. - -### 5. Verify everything is connected - -From repository root: - -```bash -curl -i http://localhost:5173/config/runtime-config.json -curl -i http://localhost:5173/api/v1/healthz/ -curl -i http://localhost:5173/api/v1/offices/ -``` - -Expected: all three return `HTTP/1.1 200 OK`. +Open `http://localhost:5173`. If that port is in use, the server automatically falls back to `5174`. -### Frontend-only checks (without backend) +### Frontend-only (without backend) -If you only want to verify SSR/frontend startup, backend is not required. -In that case, `api/v1/*` calls will fail until backend is running. +The frontend starts without a running backend. API calls to `/api/v1/*` will fail gracefully until the backend is running. ### Run Checks @@ -116,7 +77,7 @@ docker build -t appointment-booking:latest . ```bash docker run -p 5173:5173 \ - -e API_PROXY_TARGET=http://host.docker.internal:5100 \ + -e API_BASE_URL=http://api-service/api/v1 \ -e NODE_ENV=production \ appointment-booking:latest ``` @@ -137,10 +98,11 @@ docker-compose up appointment-booking | Variable | Default | Purpose | |----------|---------|---------| -| `API_PROXY_TARGET` | `http://localhost:5000` | Backend API URL for proxy (dev) or sidecar (prod) | -| `NODE_ENV` | `development` | Node environment (development/production) | +| `NODE_ENV` | `development` | Controls production vs development mode | | `PORT` | `5173` | Server listen port | -| `FALLBACK_PORT` | `5174` | Backup port used when `PORT` is already in use | +| `FALLBACK_PORT` | `PORT + 1` | Fallback port when `PORT` is already in use (dev) | +| `API_PROXY_TARGET` | `http://localhost:5000` | Backend URL for Vite's `/api/v1` dev proxy (**dev only**) | +| `API_BASE_URL` | `/api/v1` | API base URL served to the client via runtime config (**production**) | | `REQUEST_TIMEOUT_MS` | `10000` | API request timeout in milliseconds | ### Runtime Config diff --git a/appointment-booking/ci_output.txt b/appointment-booking/ci_output.txt new file mode 100644 index 000000000..495fe04a8 --- /dev/null +++ b/appointment-booking/ci_output.txt @@ -0,0 +1,510 @@ + +> appointment-booking@0.0.0 ci:check +> npm run lint && npm run type-check && npm run test && npm run build && npm run license-check + + +> appointment-booking@0.0.0 lint +> eslint . + + +> appointment-booking@0.0.0 type-check +> tsc --noEmit + + +> appointment-booking@0.0.0 test +> vitest run + + + RUN  v4.1.6 /Users/vveenu/Documents/GitHub/queue-management/appointment-booking + + βœ“ src/services/api-client.service.test.ts (2 tests) 5ms + βœ“ src/App.test.tsx (1 test) 52ms + + Test Files  2 passed (2) + Tests  3 passed (3) + Start at  16:12:08 + Duration  781ms (transform 45ms, setup 296ms, import 43ms, tests 57ms, environment 963ms) + + +> appointment-booking@0.0.0 build +> tsc -b && npm run build:client && npm run build:server + + +> appointment-booking@0.0.0 build:client +> vite build --outDir dist/client + +vite v8.0.12 building client environment for production... + transforming...βœ“ 26 modules transformed. +rendering chunks... +computing gzip size... +dist/client/index.html 0.48 kB β”‚ gzip: 0.30 kB +dist/client/assets/BCSans-LightItalic-dN5bWDr3.woff2 200.23 kB +dist/client/assets/BCSans-Italic-_P9wZbgV.woff2 201.80 kB +dist/client/assets/BCSans-Bold-Ciclm6eX.woff2 209.33 kB +dist/client/assets/BCSans-BoldItalic-bEalI5bL.woff2 215.60 kB +dist/client/assets/BCSans-Light-DTetth3X.woff2 223.74 kB +dist/client/assets/BCSans-Regular-DKwZ9GnR.woff2 253.98 kB +dist/client/assets/BCSans-LightItalic-C2AxY9aU.woff 356.58 kB +dist/client/assets/BCSans-Italic-BSFPKGgR.woff 359.68 kB +dist/client/assets/BCSans-Bold-BmoTexmK.woff 360.36 kB +dist/client/assets/BCSans-BoldItalic-Dg_86UDa.woff 378.33 kB +dist/client/assets/BCSans-Light-CmVrKSZi.woff 388.54 kB +dist/client/assets/BCSans-Regular-CMx_o1HH.woff 486.61 kB +dist/client/assets/index-CeFoQ9g5.css 9.34 kB β”‚ gzip: 2.11 kB +dist/client/assets/index-DfOt3hr1.js 356.99 kB β”‚ gzip: 95.59 kB + +βœ“ built in 111ms + +> appointment-booking@0.0.0 build:server +> vite build --ssr src/entry-server.tsx --outDir dist/server + +vite v8.0.12 building ssr environment for production... + transforming...βœ“ 10 modules transformed. +rendering chunks... +computing gzip size... +dist/server/entry-server.js 5.01 kB β”‚ gzip: 1.79 kB + +βœ“ built in 19ms + +> appointment-booking@0.0.0 license-check +> license-checker --production --onlyAllow "MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD" --excludePackages "appointment-booking@0.0.0;@bcgov/bc-sans@2.1.0" + +(node:65471) [DEP0170] DeprecationWarning: The URL git+ssh://git@github.com:bcgov/bc-sans.git is invalid. Future versions of Node.js will throw an error. +(Use `node --trace-deprecation ...` to show where the warning was created) +β”œβ”€ @adobe/react-spectrum-ui@1.2.1 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum-ui +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum-ui/LICENSE +β”œβ”€ @adobe/react-spectrum-workflow@2.3.5 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum-workflow +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum-workflow/LICENSE +β”œβ”€ @adobe/react-spectrum@3.47.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum/LICENSE +β”œβ”€ @babel/runtime@7.29.2 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/babel/babel +β”‚ β”œβ”€ publisher: The Babel Team +β”‚ β”œβ”€ url: https://babel.dev/team +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@babel/runtime +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@babel/runtime/LICENSE +β”œβ”€ @bcgov/design-system-react-components@0.7.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/bcgov/design-system +β”‚ β”œβ”€ publisher: Tyler Krys +β”‚ β”œβ”€ email: Tyler.Krys@gov.bc.ca +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-system-react-components +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-system-react-components/README.md +β”œβ”€ @bcgov/design-tokens@3.2.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/bcgov/design-system +β”‚ β”œβ”€ publisher: Tyler Krys +β”‚ β”œβ”€ email: Tyler.Krys@gov.bc.ca +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-system-react-components/node_modules/@bcgov/design-tokens +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-system-react-components/node_modules/@bcgov/design-tokens/README.md +β”œβ”€ @bcgov/design-tokens@4.0.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/bcgov/design-system +β”‚ β”œβ”€ publisher: Tyler Krys +β”‚ β”œβ”€ email: Tyler.Krys@gov.bc.ca +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-tokens +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@bcgov/design-tokens/README.md +β”œβ”€ @formatjs/ecma402-abstract@2.3.6 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ publisher: Long Ho +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/ecma402-abstract +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/ecma402-abstract/LICENSE.md +β”œβ”€ @formatjs/fast-memoize@2.2.7 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ publisher: Long Ho +β”‚ β”œβ”€ email: holevietlong@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/fast-memoize +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/fast-memoize/LICENSE.md +β”œβ”€ @formatjs/icu-messageformat-parser@2.11.4 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/icu-messageformat-parser +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/icu-messageformat-parser/LICENSE.md +β”œβ”€ @formatjs/icu-skeleton-parser@1.8.16 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/icu-skeleton-parser +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/icu-skeleton-parser/LICENSE.md +β”œβ”€ @formatjs/intl-localematcher@0.6.2 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ publisher: Long Ho +β”‚ β”œβ”€ email: holevietlong@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/intl-localematcher +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@formatjs/intl-localematcher/LICENSE.md +β”œβ”€ @internationalized/date@3.12.1 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum/tree/main/packages/@internationalized/date +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/date +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/date/LICENSE +β”œβ”€ @internationalized/message@3.1.9 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/message +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/message/LICENSE +β”œβ”€ @internationalized/number@3.6.6 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/number +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/number/LICENSE +β”œβ”€ @internationalized/string@3.2.8 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/string +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@internationalized/string/LICENSE +β”œβ”€ @react-aria/autocomplete@3.0.0-rc.6 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/autocomplete +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/autocomplete/LICENSE +β”œβ”€ @react-aria/button@3.15.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/button +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/button/LICENSE +β”œβ”€ @react-aria/collections@3.1.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/collections +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/collections/LICENSE +β”œβ”€ @react-aria/combobox@3.16.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/combobox +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/combobox/LICENSE +β”œβ”€ @react-aria/dnd@3.12.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/dnd +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/dnd/LICENSE +β”œβ”€ @react-aria/focus@3.22.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/focus +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/focus/LICENSE +β”œβ”€ @react-aria/i18n@3.13.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/i18n +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/i18n/LICENSE +β”œβ”€ @react-aria/interactions@3.28.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/interactions +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/interactions/LICENSE +β”œβ”€ @react-aria/listbox@3.16.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/listbox +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/listbox/LICENSE +β”œβ”€ @react-aria/live-announcer@3.5.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/live-announcer +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/live-announcer/LICENSE +β”œβ”€ @react-aria/overlays@3.32.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/overlays +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/overlays/LICENSE +β”œβ”€ @react-aria/searchfield@3.9.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/searchfield +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/searchfield/LICENSE +β”œβ”€ @react-aria/ssr@3.10.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/ssr +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/ssr/LICENSE +β”œβ”€ @react-aria/textfield@3.19.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/textfield +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/textfield/LICENSE +β”œβ”€ @react-aria/toolbar@3.0.0-beta.24 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/toolbar +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/toolbar/LICENSE +β”œβ”€ @react-aria/utils@3.34.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/utils +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/utils/LICENSE +β”œβ”€ @react-aria/virtualizer@4.2.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/virtualizer +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-aria/virtualizer/LICENSE +β”œβ”€ @react-spectrum/button@3.18.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/button +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/button/LICENSE +β”œβ”€ @react-spectrum/combobox@3.17.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/combobox +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/combobox/LICENSE +β”œβ”€ @react-spectrum/form@3.8.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/form +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/form/LICENSE +β”œβ”€ @react-spectrum/provider@3.11.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/provider +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/provider/LICENSE +β”œβ”€ @react-spectrum/searchfield@3.9.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/searchfield +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/searchfield/LICENSE +β”œβ”€ @react-spectrum/table@3.18.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/table +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-spectrum/table/LICENSE +β”œβ”€ @react-stately/autocomplete@3.0.0-beta.4 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/autocomplete +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/autocomplete/LICENSE +β”œβ”€ @react-stately/combobox@3.14.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/combobox +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/combobox/LICENSE +β”œβ”€ @react-stately/grid@3.12.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/grid +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/grid/LICENSE +β”œβ”€ @react-stately/layout@4.7.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/layout +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/layout/LICENSE +β”œβ”€ @react-stately/searchfield@3.6.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/searchfield +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/searchfield/LICENSE +β”œβ”€ @react-stately/selection@3.21.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/selection +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/selection/LICENSE +β”œβ”€ @react-stately/table@3.16.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/table +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/table/LICENSE +β”œβ”€ @react-stately/utils@3.12.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/utils +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/utils/LICENSE +β”œβ”€ @react-stately/virtualizer@4.5.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/virtualizer +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-stately/virtualizer/LICENSE +β”œβ”€ @react-types/autocomplete@3.0.0-alpha.38 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/autocomplete +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/autocomplete/LICENSE +β”œβ”€ @react-types/button@3.16.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/button +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/button/LICENSE +β”œβ”€ @react-types/combobox@3.15.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/combobox +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/combobox/LICENSE +β”œβ”€ @react-types/form@3.8.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/form +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/form/LICENSE +β”œβ”€ @react-types/grid@3.4.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/grid +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/grid/LICENSE +β”œβ”€ @react-types/searchfield@3.7.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/searchfield +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/searchfield/LICENSE +β”œβ”€ @react-types/shared@3.34.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/shared +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/shared/LICENSE +β”œβ”€ @react-types/table@3.14.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/table +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@react-types/table/LICENSE +β”œβ”€ @spectrum-icons/ui@3.7.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@spectrum-icons/ui +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@spectrum-icons/ui/LICENSE +β”œβ”€ @spectrum-icons/workflow@4.3.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@spectrum-icons/workflow +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@spectrum-icons/workflow/LICENSE +β”œβ”€ @swc/helpers@0.5.21 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/swc-project/swc +β”‚ β”œβ”€ publisher: κ°•λ™μœ€ +β”‚ β”œβ”€ email: kdy1997.dev@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@swc/helpers +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@swc/helpers/LICENSE +β”œβ”€ aria-hidden@1.2.6 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/theKashey/aria-hidden +β”‚ β”œβ”€ publisher: Anton Korzunov +β”‚ β”œβ”€ email: thekashey@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/aria-hidden +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/aria-hidden/LICENSE +β”œβ”€ client-only@0.0.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ └─ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/client-only +β”œβ”€ clsx@2.1.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/lukeed/clsx +β”‚ β”œβ”€ publisher: Luke Edwards +β”‚ β”œβ”€ email: luke.edwards05@gmail.com +β”‚ β”œβ”€ url: https://lukeed.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/clsx +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/clsx/license +β”œβ”€ csstype@3.2.3 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/frenic/csstype +β”‚ β”œβ”€ publisher: Fredrik Nicol +β”‚ β”œβ”€ email: fredrik.nicol@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/csstype +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/csstype/LICENSE +β”œβ”€ decimal.js@10.6.0 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/MikeMcl/decimal.js +β”‚ β”œβ”€ publisher: Michael Mclaughlin +β”‚ β”œβ”€ email: M8ch88l@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/decimal.js +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/decimal.js/LICENCE.md +β”œβ”€ dom-helpers@5.2.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/react-bootstrap/dom-helpers +β”‚ β”œβ”€ publisher: Jason Quense +β”‚ β”œβ”€ email: monastic.panic@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/dom-helpers +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/dom-helpers/LICENSE +β”œβ”€ intl-messageformat@10.7.18 +β”‚ β”œβ”€ licenses: BSD-3-Clause +β”‚ β”œβ”€ repository: https://github.com/formatjs/formatjs +β”‚ β”œβ”€ publisher: Eric Ferraiuolo +β”‚ β”œβ”€ email: eferraiuolo@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/intl-messageformat +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/intl-messageformat/LICENSE.md +β”œβ”€ js-tokens@4.0.0 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/lydell/js-tokens +β”‚ β”œβ”€ publisher: Simon Lydell +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/js-tokens +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/js-tokens/LICENSE +β”œβ”€ loose-envify@1.4.0 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/zertosh/loose-envify +β”‚ β”œβ”€ publisher: Andres Suarez +β”‚ β”œβ”€ email: zertosh@gmail.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/loose-envify +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/loose-envify/LICENSE +β”œβ”€ object-assign@4.1.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/sindresorhus/object-assign +β”‚ β”œβ”€ publisher: Sindre Sorhus +β”‚ β”œβ”€ email: sindresorhus@gmail.com +β”‚ β”œβ”€ url: sindresorhus.com +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/object-assign +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/object-assign/license +β”œβ”€ prop-types@15.8.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/facebook/prop-types +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/prop-types +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/prop-types/LICENSE +β”œβ”€ react-aria-components@1.16.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-aria-components +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-aria-components/LICENSE +β”œβ”€ react-aria-components@1.17.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum/node_modules/react-aria-components +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/@adobe/react-spectrum/node_modules/react-aria-components/LICENSE +β”œβ”€ react-aria@3.48.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-aria +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-aria/LICENSE +β”œβ”€ react-dom@19.2.6 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/facebook/react +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-dom +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-dom/LICENSE +β”œβ”€ react-is@16.13.1 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/facebook/react +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-is +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-is/LICENSE +β”œβ”€ react-stately@3.46.0 +β”‚ β”œβ”€ licenses: Apache-2.0 +β”‚ β”œβ”€ repository: https://github.com/adobe/react-spectrum +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-stately +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-stately/LICENSE +β”œβ”€ react-transition-group@4.4.5 +β”‚ β”œβ”€ licenses: BSD-3-Clause +β”‚ β”œβ”€ repository: https://github.com/reactjs/react-transition-group +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-transition-group +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react-transition-group/LICENSE +β”œβ”€ react@19.2.6 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/facebook/react +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/react/LICENSE +β”œβ”€ scheduler@0.27.0 +β”‚ β”œβ”€ licenses: MIT +β”‚ β”œβ”€ repository: https://github.com/facebook/react +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/scheduler +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/scheduler/LICENSE +β”œβ”€ tslib@2.8.1 +β”‚ β”œβ”€ licenses: 0BSD +β”‚ β”œβ”€ repository: https://github.com/Microsoft/tslib +β”‚ β”œβ”€ publisher: Microsoft Corp. +β”‚ β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/tslib +β”‚ └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/tslib/LICENSE.txt +└─ use-sync-external-store@1.6.0 + β”œβ”€ licenses: MIT + β”œβ”€ repository: https://github.com/facebook/react + β”œβ”€ path: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/use-sync-external-store + └─ licenseFile: /Users/vveenu/Documents/GitHub/queue-management/appointment-booking/node_modules/use-sync-external-store/LICENSE + diff --git a/appointment-booking/package-lock.json b/appointment-booking/package-lock.json index 20eb76452..5bb1d81fe 100644 --- a/appointment-booking/package-lock.json +++ b/appointment-booking/package-lock.json @@ -27,7 +27,6 @@ "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", @@ -52,6 +51,7 @@ "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.47.0.tgz", "integrity": "sha512-EDQuMzz0kUeiMUUlxoeLFQyyxOXaAC7qlBw2PYOUfFLYd87xcV7VVV0JxiYx8zGk1IIY3UgQHgXrS1fv7CgezQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@internationalized/date": "^3.12.1", "@react-types/shared": "^3.34.0", @@ -191,6 +191,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -409,6 +410,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bcgov/bc-sans": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@bcgov/bc-sans/-/bc-sans-2.1.0.tgz", + "integrity": "sha512-1MesF4NAVpM5dywoJ68wNcBylHbPqg1dDV/FNuQm0BbspETGlPmfX8LG8rtrCjCAPhWuL2qRT/lBYDUMvFTUnw==", + "license": "SIL", + "peer": true + }, "node_modules/@bcgov/design-system-react-components": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@bcgov/design-system-react-components/-/design-system-react-components-0.7.0.tgz", @@ -540,6 +548,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -588,33 +597,11 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1323,6 +1310,21 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@react-spectrum/provider": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.11.0.tgz", + "integrity": "sha512-W2Gxbj8AcG5OR2K5Ua3K8qQqxdsiytEiz+2rhr6oQyBM8VafEgDcNPYSOTtfjrQM3snl2Uhp8LzwN0jwQe/6nQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@adobe/react-spectrum": "3.47.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@react-spectrum/searchfield": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/@react-spectrum/searchfield/-/searchfield-3.9.0.tgz", @@ -2064,6 +2066,7 @@ "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2074,6 +2077,7 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2084,6 +2088,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2133,6 +2138,7 @@ "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", @@ -2483,6 +2489,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2562,23 +2569,6 @@ "node": ">= 0.4" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -2589,127 +2579,6 @@ "node": ">=0.10.0" } }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2727,32 +2596,6 @@ "node": ">=12" } }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -2819,6 +2662,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2833,56 +2677,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/call-bind": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", - "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "get-intrinsic": "^1.3.0", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001792", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", @@ -3041,60 +2835,6 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3137,42 +2877,6 @@ "dev": true, "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3204,19 +2908,6 @@ "wrappy": "1" } }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -3234,21 +2925,6 @@ "csstype": "^3.0.2" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.354", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.354.tgz", @@ -3269,119 +2945,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/es-abstract": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", - "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", - "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0" - }, "engines": { "node": ">= 0.4" } @@ -3393,66 +2962,6 @@ "dev": true, "license": "MIT" }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3482,6 +2991,7 @@ "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -3538,6 +3048,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3579,39 +3090,6 @@ } } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, "node_modules/eslint-plugin-react-hooks": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", @@ -3642,37 +3120,6 @@ "eslint": "^9 || ^10" } }, - "node_modules/eslint-plugin-react/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", @@ -3886,22 +3333,6 @@ "dev": true, "license": "ISC" }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3934,47 +3365,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3985,63 +3375,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4121,36 +3454,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4158,19 +3461,6 @@ "dev": true, "license": "ISC" }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4181,64 +3471,6 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", @@ -4338,382 +3570,27 @@ "dev": true, "license": "ISC" }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/intl-messageformat": { "version": "10.7.18", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", - "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.4", - "tslib": "^2.8.0" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "license": "BSD-3-Clause", "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" + "hasown": "^2.0.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4721,43 +3598,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, @@ -4768,24 +3635,6 @@ "dev": true, "license": "ISC" }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4897,22 +3746,6 @@ "node": ">=6" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5298,16 +4131,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/mdn-data": { "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", @@ -5397,25 +4220,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/node-releases": { "version": "2.0.44", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", @@ -5498,104 +4302,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -5667,24 +4373,6 @@ "os-tmpdir": "^1.0.0" } }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5787,6 +4475,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5794,16 +4483,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/postcss": { "version": "8.5.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", @@ -5849,6 +4528,7 @@ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5933,6 +4613,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -6004,6 +4685,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6121,50 +4803,6 @@ "node": ">=8" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -6175,30 +4813,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/rolldown": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", @@ -6230,71 +4844,16 @@ "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", - "@rolldown/binding-win32-x64-msvc": "1.0.0" - } - }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", - "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/safe-array-concat": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", - "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@rolldown/binding-win32-x64-msvc": "1.0.0" } }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", + "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -6324,55 +4883,6 @@ "semver": "bin/semver.js" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6396,82 +4906,6 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -6580,118 +5014,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -6886,90 +5208,13 @@ "node": ">= 0.8.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typescript": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7002,25 +5247,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/undici": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", @@ -7112,6 +5338,7 @@ "integrity": "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -7338,95 +5565,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -7504,6 +5642,7 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/appointment-booking/package.json b/appointment-booking/package.json index c6429912a..941256440 100644 --- a/appointment-booking/package.json +++ b/appointment-booking/package.json @@ -17,7 +17,7 @@ "start": "NODE_ENV=production node server.js", "type-check": "tsc --noEmit", "preview": "npm run start", - "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0\"", + "license-check": "license-checker --production --onlyAllow \"MIT;Apache-2.0;ISC;BSD-2-Clause;BSD-3-Clause;0BSD\" --excludePackages \"appointment-booking@0.0.0;@bcgov/bc-sans@2.1.0\"", "ci:check": "npm run lint && npm run type-check && npm run test && npm run build && npm run license-check" }, "dependencies": { @@ -40,7 +40,6 @@ "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", diff --git a/appointment-booking/server.js b/appointment-booking/server.js index f476d08eb..34913e765 100644 --- a/appointment-booking/server.js +++ b/appointment-booking/server.js @@ -7,18 +7,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)) const isProd = process.env.NODE_ENV === 'production' const preferredPort = Number(process.env.PORT || 5173) const fallbackPort = Number(process.env.FALLBACK_PORT || preferredPort + 1) -const apiProxyTarget = (process.env.API_PROXY_TARGET || 'http://localhost:5000').replace(/\/$/, '') - -const mimeTypes = { - '.css': 'text/css; charset=utf-8', - '.ico': 'image/x-icon', - '.js': 'text/javascript; charset=utf-8', - '.json': 'application/json; charset=utf-8', - '.png': 'image/png', - '.svg': 'image/svg+xml', - '.woff': 'font/woff', - '.woff2': 'font/woff2', -} let vite if (!isProd) { @@ -41,53 +29,6 @@ function send(res, statusCode, body, headers = {}) { res.end(body) } -function getRequestBody(req) { - return new Promise((resolve, reject) => { - const chunks = [] - req.on('data', (chunk) => chunks.push(chunk)) - req.on('end', () => { - if (!chunks.length) { - resolve(undefined) - return - } - - resolve(Buffer.concat(chunks)) - }) - req.on('error', reject) - }) -} - -async function tryProxyApiRequest(req, res, urlPath) { - if (!urlPath.startsWith('/api/v1')) { - return false - } - - const targetUrl = `${apiProxyTarget}${urlPath}` - const requestBody = await getRequestBody(req) - - try { - const proxyResponse = await fetch(targetUrl, { - method: req.method, - headers: { - ...(req.headers || {}), - host: undefined, - }, - body: requestBody, - }) - - const responseBuffer = Buffer.from(await proxyResponse.arrayBuffer()) - send(res, proxyResponse.status, responseBuffer, { - 'Content-Type': proxyResponse.headers.get('content-type') || 'application/json; charset=utf-8', - }) - return true - } catch { - send(res, 502, JSON.stringify({ message: 'Unable to reach booking API' }), { - 'Content-Type': 'application/json; charset=utf-8', - }) - return true - } -} - function getRuntimeConfig() { return { apiBaseUrl: process.env.API_BASE_URL || process.env.VITE_API_BASE_URL || '/api/v1', @@ -142,16 +83,6 @@ async function tryServeStatic(urlPath, res) { const server = http.createServer(async (req, res) => { const url = req.url || '/' - try { - const proxied = await tryProxyApiRequest(req, res, url) - if (proxied) { - return - } - } catch { - send(res, 500, 'Internal Server Error') - return - } - if (url.startsWith('/config/runtime-config.json')) { send(res, 200, JSON.stringify(getRuntimeConfig()), { 'Cache-Control': 'no-store', diff --git a/appointment-booking/src/components/common/Button.tsx b/appointment-booking/src/components/common/Button.tsx deleted file mode 100644 index 15489ce0e..000000000 --- a/appointment-booking/src/components/common/Button.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react' -import { Button as DSButton } from '@bcgov/design-system-react-components' - -export function Button(props: React.ComponentPropsWithoutRef) { - return -} diff --git a/appointment-booking/src/components/common/Layout.tsx b/appointment-booking/src/components/common/Layout.tsx index 6f8886c36..6001a5dc2 100644 --- a/appointment-booking/src/components/common/Layout.tsx +++ b/appointment-booking/src/components/common/Layout.tsx @@ -1,3 +1,4 @@ +import { Footer, Header } from '@bcgov/design-system-react-components' import React from 'react' interface LayoutProps { @@ -6,21 +7,19 @@ interface LayoutProps { export function Layout({ children }: LayoutProps) { return ( -
-
-
- - - BC Government Logo - -
-
-
{children}
-
-
-

© 2026 Province of British Columbia

-
-
-
+ <> +
+ Skip to main content + , + ]} + /> +
+ {children} +
+