root: automate system migrations, move docker to lifecycle folder
This commit is contained in:
		
							
								
								
									
										23
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Dockerfile
									
									
									
									
									
								
							@ -11,27 +11,22 @@ RUN pip install pipenv && \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FROM python:3.8-slim-buster
 | 
					FROM python:3.8-slim-buster
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY --from=locker /app/requirements.txt /app/
 | 
					WORKDIR /
 | 
				
			||||||
COPY --from=locker /app/requirements-dev.txt /app/
 | 
					COPY --from=locker /app/requirements.txt /
 | 
				
			||||||
 | 
					COPY --from=locker /app/requirements-dev.txt /
 | 
				
			||||||
WORKDIR /app/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN apt-get update && \
 | 
					RUN apt-get update && \
 | 
				
			||||||
    apt-get install -y --no-install-recommends postgresql-client-11 build-essential && \
 | 
					    apt-get install -y --no-install-recommends postgresql-client-11 build-essential && \
 | 
				
			||||||
    rm -rf /var/lib/apt/ && \
 | 
					    rm -rf /var/lib/apt/ && \
 | 
				
			||||||
    pip install -r requirements.txt  --no-cache-dir && \
 | 
					    pip install -r /requirements.txt  --no-cache-dir && \
 | 
				
			||||||
    apt-get remove --purge -y build-essential && \
 | 
					    apt-get remove --purge -y build-essential && \
 | 
				
			||||||
    apt-get autoremove --purge && \
 | 
					    apt-get autoremove --purge && \
 | 
				
			||||||
    adduser --system --no-create-home --uid 1000 --group --home /app passbook
 | 
					    adduser --system --no-create-home --uid 1000 --group --home /passbook passbook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY ./passbook/ /app/passbook
 | 
					COPY ./passbook/ /passbook
 | 
				
			||||||
COPY ./manage.py /app/
 | 
					COPY ./manage.py /
 | 
				
			||||||
COPY ./docker/gunicorn.conf.py /app/
 | 
					COPY ./lifecycle/ /lifecycle
 | 
				
			||||||
COPY ./docker/bootstrap.sh /bootstrap.sh
 | 
					 | 
				
			||||||
COPY ./docker/wait_for_db.py /app/wait_for_db.py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WORKDIR /app/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
USER passbook
 | 
					USER passbook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENTRYPOINT [ "/bootstrap.sh" ]
 | 
					ENTRYPOINT [ "/lifecycle/bootstrap.sh" ]
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ wget https://raw.githubusercontent.com/BeryJu/passbook/master/docker-compose.yml
 | 
				
			|||||||
# export PG_PASS=$(pwgen 40 1)
 | 
					# export PG_PASS=$(pwgen 40 1)
 | 
				
			||||||
docker-compose pull
 | 
					docker-compose pull
 | 
				
			||||||
docker-compose up -d
 | 
					docker-compose up -d
 | 
				
			||||||
docker-compose exec server ./manage.py migrate
 | 
					docker-compose run --rm server migrate
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://passbook.beryju.org//installation/kubernetes/)
 | 
					For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://passbook.beryju.org//installation/kubernetes/)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
#!/bin/bash -e
 | 
					 | 
				
			||||||
/app/wait_for_db.py
 | 
					 | 
				
			||||||
printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@"
 | 
					 | 
				
			||||||
if [[ "$1" == "server" ]]; then
 | 
					 | 
				
			||||||
    gunicorn -c gunicorn.conf.py passbook.root.asgi:application
 | 
					 | 
				
			||||||
elif [[ "$1" == "worker" ]]; then
 | 
					 | 
				
			||||||
    celery worker --autoscale=10,3 -E -B -A=passbook.root.celery -s=/tmp/celerybeat-schedule
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
    ./manage.py "$@"
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
@ -2,13 +2,13 @@
 | 
				
			|||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.test import override_settings
 | 
					from django.test import override_settings
 | 
				
			||||||
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
 | 
					from docker.models.containers import Container
 | 
				
			||||||
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
from selenium.webdriver.support import expected_conditions as ec
 | 
					from selenium.webdriver.support import expected_conditions as ec
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					 | 
				
			||||||
from docker.models.containers import Container
 | 
					 | 
				
			||||||
from docker.types import Healthcheck
 | 
					 | 
				
			||||||
from e2e.utils import USER, SeleniumTestCase
 | 
					from e2e.utils import USER, SeleniumTestCase
 | 
				
			||||||
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
 | 
					from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
 | 
				
			||||||
from passbook.policies.expression.models import ExpressionPolicy
 | 
					from passbook.policies.expression.models import ExpressionPolicy
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
"""test OAuth Provider flow"""
 | 
					"""test OAuth Provider flow"""
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					 | 
				
			||||||
from selenium.webdriver.common.keys import Keys
 | 
					 | 
				
			||||||
from structlog import get_logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
from docker.models.containers import Container
 | 
					from docker.models.containers import Container
 | 
				
			||||||
from docker.types import Healthcheck
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
 | 
					from selenium.webdriver.common.keys import Keys
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from e2e.utils import USER, SeleniumTestCase
 | 
					from e2e.utils import USER, SeleniumTestCase
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.flows.models import Flow
 | 
					from passbook.flows.models import Flow
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""test OAuth2 OpenID Provider flow"""
 | 
					"""test OAuth2 OpenID Provider flow"""
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
 | 
					from docker.models.containers import Container
 | 
				
			||||||
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
from selenium.webdriver.common.keys import Keys
 | 
					from selenium.webdriver.common.keys import Keys
 | 
				
			||||||
from selenium.webdriver.support import expected_conditions as ec
 | 
					from selenium.webdriver.support import expected_conditions as ec
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					 | 
				
			||||||
from docker.models.containers import Container
 | 
					 | 
				
			||||||
from docker.types import Healthcheck
 | 
					 | 
				
			||||||
from e2e.utils import USER, SeleniumTestCase
 | 
					from e2e.utils import USER, SeleniumTestCase
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.crypto.models import CertificateKeyPair
 | 
					from passbook.crypto.models import CertificateKeyPair
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
"""test SAML Provider flow"""
 | 
					"""test SAML Provider flow"""
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					 | 
				
			||||||
from selenium.webdriver.common.keys import Keys
 | 
					 | 
				
			||||||
from structlog import get_logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
from docker.models.containers import Container
 | 
					from docker.models.containers import Container
 | 
				
			||||||
from docker.types import Healthcheck
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
 | 
					from selenium.webdriver.common.keys import Keys
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from e2e.utils import USER, SeleniumTestCase
 | 
					from e2e.utils import USER, SeleniumTestCase
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.crypto.models import CertificateKeyPair
 | 
					from passbook.crypto.models import CertificateKeyPair
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""test SAML Source"""
 | 
					"""test SAML Source"""
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
 | 
					from docker.models.containers import Container
 | 
				
			||||||
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
from selenium.webdriver.common.keys import Keys
 | 
					from selenium.webdriver.common.keys import Keys
 | 
				
			||||||
from selenium.webdriver.support import expected_conditions as ec
 | 
					from selenium.webdriver.support import expected_conditions as ec
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					 | 
				
			||||||
from docker.models.containers import Container
 | 
					 | 
				
			||||||
from docker.types import Healthcheck
 | 
					 | 
				
			||||||
from e2e.utils import SeleniumTestCase
 | 
					from e2e.utils import SeleniumTestCase
 | 
				
			||||||
from passbook.crypto.models import CertificateKeyPair
 | 
					from passbook.crypto.models import CertificateKeyPair
 | 
				
			||||||
from passbook.flows.models import Flow
 | 
					from passbook.flows.models import Flow
 | 
				
			||||||
 | 
				
			|||||||
@ -2,15 +2,15 @@
 | 
				
			|||||||
from os.path import abspath
 | 
					from os.path import abspath
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from docker import DockerClient, from_env
 | 
				
			||||||
 | 
					from docker.models.containers import Container
 | 
				
			||||||
 | 
					from docker.types import Healthcheck
 | 
				
			||||||
from selenium.webdriver.common.by import By
 | 
					from selenium.webdriver.common.by import By
 | 
				
			||||||
from selenium.webdriver.common.keys import Keys
 | 
					from selenium.webdriver.common.keys import Keys
 | 
				
			||||||
from selenium.webdriver.support import expected_conditions as ec
 | 
					from selenium.webdriver.support import expected_conditions as ec
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
from yaml import safe_dump
 | 
					from yaml import safe_dump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from docker import DockerClient, from_env
 | 
					 | 
				
			||||||
from docker.models.containers import Container
 | 
					 | 
				
			||||||
from docker.types import Healthcheck
 | 
					 | 
				
			||||||
from e2e.utils import SeleniumTestCase
 | 
					from e2e.utils import SeleniumTestCase
 | 
				
			||||||
from passbook.flows.models import Flow
 | 
					from passbook.flows.models import Flow
 | 
				
			||||||
from passbook.providers.oauth2.generators import generate_client_secret
 | 
					from passbook.providers.oauth2.generators import generate_client_secret
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										0
									
								
								lifecycle/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								lifecycle/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										14
									
								
								lifecycle/bootstrap.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								lifecycle/bootstrap.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					#!/bin/bash -e
 | 
				
			||||||
 | 
					python -m lifecycle.wait_for_db
 | 
				
			||||||
 | 
					printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@"
 | 
				
			||||||
 | 
					if [[ "$1" == "server" ]]; then
 | 
				
			||||||
 | 
					    gunicorn -c /lifecycle/gunicorn.conf.py passbook.root.asgi:application
 | 
				
			||||||
 | 
					elif [[ "$1" == "worker" ]]; then
 | 
				
			||||||
 | 
					    celery worker --autoscale=10,3 -E -B -A=passbook.root.celery -s=/tmp/celerybeat-schedule
 | 
				
			||||||
 | 
					elif [[ "$1" == "migrate" ]]; then
 | 
				
			||||||
 | 
					    # Run system migrations first, run normal migrations after
 | 
				
			||||||
 | 
					    python -m lifecycle.migrate
 | 
				
			||||||
 | 
					    python -m manage migrate
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    python -m manage "$@"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
							
								
								
									
										55
									
								
								lifecycle/migrate.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										55
									
								
								lifecycle/migrate.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					"""System Migration handler"""
 | 
				
			||||||
 | 
					from importlib.util import module_from_spec, spec_from_file_location
 | 
				
			||||||
 | 
					from inspect import getmembers, isclass
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from psycopg2 import connect
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseMigration:
 | 
				
			||||||
 | 
					    """Base System Migration"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur: Any
 | 
				
			||||||
 | 
					    con: Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, cur: Any, con: Any):
 | 
				
			||||||
 | 
					        self.cur = cur
 | 
				
			||||||
 | 
					        self.con = con
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def needs_migration(self) -> bool:
 | 
				
			||||||
 | 
					        """Return true if Migration needs to be run"""
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        """Run the actual migration"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn = connect(
 | 
				
			||||||
 | 
					        dbname=CONFIG.y("postgresql.name"),
 | 
				
			||||||
 | 
					        user=CONFIG.y("postgresql.user"),
 | 
				
			||||||
 | 
					        password=CONFIG.y("postgresql.password"),
 | 
				
			||||||
 | 
					        host=CONFIG.y("postgresql.host"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    curr = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for migration in Path(__file__).parent.absolute().glob("system_migrations/*.py"):
 | 
				
			||||||
 | 
					        spec = spec_from_file_location("lifecycle.system_migrations", migration)
 | 
				
			||||||
 | 
					        mod = module_from_spec(spec)
 | 
				
			||||||
 | 
					        # pyright: reportGeneralTypeIssues=false
 | 
				
			||||||
 | 
					        spec.loader.exec_module(mod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for _, sub in getmembers(mod, isclass):
 | 
				
			||||||
 | 
					            migration = sub(curr, conn)
 | 
				
			||||||
 | 
					            if migration.needs_migration():
 | 
				
			||||||
 | 
					                LOGGER.info("Migration needs to be applied", migration=sub)
 | 
				
			||||||
 | 
					                migration.run()
 | 
				
			||||||
 | 
					                LOGGER.info("Migration finished applying", migration=sub)
 | 
				
			||||||
							
								
								
									
										50
									
								
								lifecycle/system_migrations/to_0_10.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								lifecycle/system_migrations/to_0_10.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					from os import system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lifecycle.migrate import BaseMigration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SQL_STATEMENT = """delete from django_migrations where app = 'passbook_stages_prompt';
 | 
				
			||||||
 | 
					drop table passbook_stages_prompt_prompt cascade;
 | 
				
			||||||
 | 
					drop table passbook_stages_prompt_promptstage cascade;
 | 
				
			||||||
 | 
					drop table passbook_stages_prompt_promptstage_fields;
 | 
				
			||||||
 | 
					drop table corsheaders_corsmodel cascade;
 | 
				
			||||||
 | 
					drop table oauth2_provider_accesstoken cascade;
 | 
				
			||||||
 | 
					drop table oauth2_provider_grant cascade;
 | 
				
			||||||
 | 
					drop table oauth2_provider_refreshtoken cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_client cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_client_response_types cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_code cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_responsetype cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_rsakey cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_token cascade;
 | 
				
			||||||
 | 
					drop table oidc_provider_userconsent cascade;
 | 
				
			||||||
 | 
					drop table passbook_providers_app_gw_applicationgatewayprovider cascade;
 | 
				
			||||||
 | 
					delete from django_migrations where app = 'passbook_flows' and name = '0008_default_flows';
 | 
				
			||||||
 | 
					delete from django_migrations where app = 'passbook_flows' and name = '0009_source_flows';
 | 
				
			||||||
 | 
					delete from django_migrations where app = 'passbook_flows' and name = '0010_provider_flows';
 | 
				
			||||||
 | 
					delete from django_migrations where app = 'passbook_stages_password' and
 | 
				
			||||||
 | 
					name = '0002_passwordstage_change_flow';"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class To010Migration(BaseMigration):
 | 
				
			||||||
 | 
					    def needs_migration(self) -> bool:
 | 
				
			||||||
 | 
					        self.cur.execute(
 | 
				
			||||||
 | 
					            "select * from information_schema.tables where table_name='oidc_provider_client'"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return bool(self.cur.rowcount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def system_crit(self, command):
 | 
				
			||||||
 | 
					        retval = system(command)  # nosec
 | 
				
			||||||
 | 
					        if retval != 0:
 | 
				
			||||||
 | 
					            raise Exception("Migration error")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        self.cur.execute(SQL_STATEMENT)
 | 
				
			||||||
 | 
					        self.con.commit()
 | 
				
			||||||
 | 
					        self.system_crit("./manage.py migrate passbook_stages_prompt")
 | 
				
			||||||
 | 
					        self.system_crit("./manage.py migrate passbook_flows 0008_default_flows --fake")
 | 
				
			||||||
 | 
					        self.system_crit("./manage.py migrate passbook_flows 0009_source_flows --fake")
 | 
				
			||||||
 | 
					        self.system_crit(
 | 
				
			||||||
 | 
					            "./manage.py migrate passbook_flows 0010_provider_flows --fake"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.system_crit("./manage.py migrate passbook_flows")
 | 
				
			||||||
 | 
					        self.system_crit("./manage.py migrate passbook_stages_password --fake")
 | 
				
			||||||
							
								
								
									
										12
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								swagger.yaml
									
									
									
									
									
								
							@ -5249,7 +5249,7 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - stages
 | 
					        - stages
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /stages/prompt/stages/{pbm_uuid}/:
 | 
					  /stages/prompt/stages/{stage_uuid}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: stages_prompt_stages_read
 | 
					      operationId: stages_prompt_stages_read
 | 
				
			||||||
      description: PromptStage Viewset
 | 
					      description: PromptStage Viewset
 | 
				
			||||||
@ -5303,7 +5303,7 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - stages
 | 
					        - stages
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
      - name: pbm_uuid
 | 
					      - name: stage_uuid
 | 
				
			||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
        description: A UUID string identifying this Prompt Stage.
 | 
					        description: A UUID string identifying this Prompt Stage.
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
@ -7463,7 +7463,7 @@ definitions:
 | 
				
			|||||||
    type: object
 | 
					    type: object
 | 
				
			||||||
    properties:
 | 
					    properties:
 | 
				
			||||||
      pk:
 | 
					      pk:
 | 
				
			||||||
        title: Pbm uuid
 | 
					        title: Stage uuid
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        readOnly: true
 | 
					        readOnly: true
 | 
				
			||||||
@ -7477,6 +7477,12 @@ definitions:
 | 
				
			|||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
          format: uuid
 | 
					          format: uuid
 | 
				
			||||||
        uniqueItems: true
 | 
					        uniqueItems: true
 | 
				
			||||||
 | 
					      validation_policies:
 | 
				
			||||||
 | 
					        type: array
 | 
				
			||||||
 | 
					        items:
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					          format: uuid
 | 
				
			||||||
 | 
					        uniqueItems: true
 | 
				
			||||||
  UserDeleteStage:
 | 
					  UserDeleteStage:
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
      - name
 | 
					      - name
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user