diff --git a/.github/scripts/version_check.py b/.github/scripts/version_check.py index 8f657edcd2..a101c0bd12 100644 --- a/.github/scripts/version_check.py +++ b/.github/scripts/version_check.py @@ -296,10 +296,6 @@ def main() -> bool: print('GITHUB_REF:', GITHUB_REF) return False - if docker_tags is None: - print('Docker tags could not be determined') - return False - print(f"Version check passed for '{inventree_version}'!") print(f"Docker tags: '{docker_tags}'") diff --git a/contrib/container/init.sh b/contrib/container/init.sh index 4883c49b0b..f491d390c4 100644 --- a/contrib/container/init.sh +++ b/contrib/container/init.sh @@ -25,7 +25,7 @@ if [[ ! -d "$INVENTREE_BACKUP_DIR" ]]; then fi # Check if "config.yaml" has been copied into the correct location -if test -f "$INVENTREE_CONFIG_FILE"; then +if [[ -f "$INVENTREE_CONFIG_FILE" ]]; then echo "Loading config file : $INVENTREE_CONFIG_FILE" else echo "Copying config file from $INVENTREE_BACKEND_DIR/InvenTree/config_template.yml to $INVENTREE_CONFIG_FILE" @@ -37,7 +37,7 @@ fi # so that the installed modules persist! if [[ -n "$INVENTREE_PY_ENV" ]]; then - if test -d "$INVENTREE_PY_ENV"; then + if [[ -d "$INVENTREE_PY_ENV" ]]; then # venv already exists echo "Using Python virtual environment: ${INVENTREE_PY_ENV}" source ${INVENTREE_PY_ENV}/bin/activate diff --git a/contrib/install.sh b/contrib/install.sh index ec8ee085e5..88991514a8 100755 --- a/contrib/install.sh +++ b/contrib/install.sh @@ -26,23 +26,23 @@ root_command() { } function get_distribution { - if [ -f /etc/os-release ]; then + if [[ -f /etc/os-release ]]; then . /etc/os-release OS=$NAME VER=$VERSION_ID elif type lsb_release >/dev/null 2>&1; then OS=$(lsb_release -si) VER=$(lsb_release -sr) - elif [ -f /etc/lsb-release ]; then + elif [[ -f /etc/lsb-release ]]; then . /etc/lsb-release OS=$DISTRIB_ID VER=$DISTRIB_RELEASE - elif [ -f /etc/debian_version ]; then + elif [[ -f /etc/debian_version ]]; then OS=Debian VER=$(cat /etc/debian_version) - elif [ -f /etc/SuSe-release ]; then + elif [[ -f /etc/SuSe-release ]]; then OS=SEL - elif [ -f /etc/redhat-release ]; then + elif [[ -f /etc/redhat-release ]]; then OS=RedHat else OS=$(uname -s) diff --git a/contrib/installer/src/root_command.sh b/contrib/installer/src/root_command.sh index c10ff60be1..faef5bcd1a 100644 --- a/contrib/installer/src/root_command.sh +++ b/contrib/installer/src/root_command.sh @@ -16,23 +16,23 @@ function do_call() { } function get_distribution { - if [ -f /etc/os-release ]; then + if [[ -f /etc/os-release ]]; then . /etc/os-release OS=$NAME VER=$VERSION_ID elif type lsb_release >/dev/null 2>&1; then OS=$(lsb_release -si) VER=$(lsb_release -sr) - elif [ -f /etc/lsb-release ]; then + elif [[ -f /etc/lsb-release ]]; then . /etc/lsb-release OS=$DISTRIB_ID VER=$DISTRIB_RELEASE - elif [ -f /etc/debian_version ]; then + elif [[ -f /etc/debian_version ]]; then OS=Debian VER=$(cat /etc/debian_version) - elif [ -f /etc/SuSe-release ]; then + elif [[ -f /etc/SuSe-release ]]; then OS=SEL - elif [ -f /etc/redhat-release ]; then + elif [[ -f /etc/redhat-release ]]; then OS=RedHat else OS=$(uname -s) diff --git a/contrib/packager.io/before.sh b/contrib/packager.io/before.sh index 3ed395d08b..2f9bd19071 100755 --- a/contrib/packager.io/before.sh +++ b/contrib/packager.io/before.sh @@ -55,7 +55,7 @@ echo "### VERSION ###" echo "INFO frontend | Trying to get frontend" # Check if tag sha is the same as the commit sha TAG_SHA=$(jq -r '.sha' tag.json) -if [ "$TAG_SHA" != "$FULL_SHA" ]; then +if [[ "$TAG_SHA" != "$FULL_SHA" ]]; then echo "INFO frontend | Tag sha '$TAG_SHA' is not the same as commit sha $FULL_SHA, can not download frontend" else echo "INFO frontend | Getting frontend from github via tag" diff --git a/contrib/packager.io/functions.sh b/contrib/packager.io/functions.sh index 0bb9a21961..13dc24e5fc 100755 --- a/contrib/packager.io/functions.sh +++ b/contrib/packager.io/functions.sh @@ -8,7 +8,7 @@ PYTHON_FROM=11 PYTHON_TO=15 function detect_docker() { - if [ -n "$(grep docker /dev/null)" ]; then + if [[ -n "$(which systemctl 2>/dev/null)" ]]; then INIT_CMD="systemctl" - elif [ -n "$(which initctl 2>/dev/null)" ]; then + elif [[ -n "$(which initctl 2>/dev/null)" ]]; then INIT_CMD="initctl" else function sysvinit() { @@ -28,7 +28,7 @@ function detect_initcmd() { INIT_CMD="sysvinit" fi - if [ "${DOCKER}" == "yes" ]; then + if [[ "${DOCKER}" == "yes" ]]; then INIT_CMD="initctl" fi echo "# POI05| Using init command: ${INIT_CMD}" @@ -37,7 +37,7 @@ function detect_initcmd() { function detect_ip() { # Get the IP address of the server - if [ "${SETUP_NO_CALLS}" == "true" ]; then + if [[ "${SETUP_NO_CALLS}" == "true" ]]; then # Use local IP address echo "# POI06| Getting the IP address of the first local IP address" export INVENTREE_IP=$(hostname -I | awk '{print $1}') @@ -52,7 +52,7 @@ function detect_ip() { function detect_python() { # Detect if there is already a python version installed in /opt/inventree/env/lib - if test -f "${APP_HOME}/env/bin/python"; then + if [[ -f "${APP_HOME}/env/bin/python" ]]; then echo "# POI07| Python environment already present" # Extract earliest python version initialised from /opt/inventree/env/lib SETUP_PYTHON=$(ls -1 ${APP_HOME}/env/bin/python* | sort | head -n 1) @@ -62,11 +62,11 @@ function detect_python() { fi # Try to detect a python between lowest and highest supported in reverse order - if [ -z "$(which ${SETUP_PYTHON})" ]; then + if [[ -z "$(which ${SETUP_PYTHON})" ]]; then echo "# POI07| Trying to detecting python3.${PYTHON_FROM} to python3.${PYTHON_TO} - using newest version" for i in $(seq $PYTHON_TO -1 $PYTHON_FROM); do echo "# POI07| Checking for python3.${i}" - if [ -n "$(which python3.${i})" ]; then + if [[ -n "$(which python3.${i})" ]]; then SETUP_PYTHON="python3.${i}" echo "# POI07| Found python3.${i} installed - using for setup ${SETUP_PYTHON}" break @@ -75,7 +75,7 @@ function detect_python() { fi # Ensure python can be executed - abort if not - if [ -z "$(which ${SETUP_PYTHON})" ]; then + if [[ -z "$(which ${SETUP_PYTHON})" ]]; then echo "${On_Red}" echo "# POI07| Python ${SETUP_PYTHON} not found - aborting!" echo "# POI07| Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'." @@ -91,14 +91,14 @@ function get_env() { envname=$1 pid=$$ - while [ -z "${!envname}" -a $pid != 1 ]; do + while [[ -z "${!envname}" && $pid != 1 ]]; do ppid=`ps -oppid -p$pid|tail -1|awk '{print $1}'` env=`strings /proc/$ppid/environ` export $envname=`echo "$env"|awk -F= '$1 == "'$envname'" { print $2; }'` pid=$ppid done - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI02| Done getting env $envname: ${!envname}" fi } @@ -106,7 +106,7 @@ function get_env() { function detect_local_env() { # Get all possible envs for the install - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI02| Printing local envs - before #++#" printenv fi @@ -116,13 +116,13 @@ function detect_local_env() { get_env $i done - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI02| Printing local envs - after #++#" printenv fi # Print branch and dir from VERSION file - if [ -f "${APP_HOME}/VERSION" ]; then + if [[ -f "${APP_HOME}/VERSION" ]]; then echo "# POI02| Loading environment variables from VERSION file" content=$(cat "${APP_HOME}/VERSION") # use grep to get the branch and target @@ -141,7 +141,7 @@ function detect_envs() { export INVENTREE_CONFIG_FILE=${INVENTREE_CONFIG_FILE:-${CONF_DIR}/config.yaml} - if test -f "${INVENTREE_CONFIG_FILE}"; then + if [[ -f "${INVENTREE_CONFIG_FILE}" ]]; then echo "# POI03| Using existing config file: ${INVENTREE_CONFIG_FILE}" # Install parser @@ -168,9 +168,9 @@ function detect_envs() { export INVENTREE_DB_PORT=$(jq -r '.[].database.PORT' <<< ${INVENTREE_CONF_DATA}) # Parse site URL if not already set - if [ -z "${INVENTREE_SITE_URL}" ]; then + if [[ -z "${INVENTREE_SITE_URL}" ]]; then # Try to read out the app config - if [ -n "$(inventree config:get INVENTREE_SITE_URL)" ]; then + if [[ -n "$(inventree config:get INVENTREE_SITE_URL)" ]]; then echo "# POI03| Getting site URL from app config" export INVENTREE_SITE_URL=$(inventree config:get INVENTREE_SITE_URL) else @@ -181,7 +181,7 @@ function detect_envs() { else echo "# POI03| No config file found: ${INVENTREE_CONFIG_FILE}, using envs or defaults" - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI03| Print current envs" printenv | grep INVENTREE_ printenv | grep SETUP_ @@ -218,7 +218,7 @@ function detect_envs() { echo "# POI03| INVENTREE_DB_ENGINE=${INVENTREE_DB_ENGINE}" echo "# POI03| INVENTREE_DB_NAME=${INVENTREE_DB_NAME}" echo "# POI03| INVENTREE_DB_USER=${INVENTREE_DB_USER}" - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI03| INVENTREE_DB_PASSWORD=${INVENTREE_DB_PASSWORD}" fi echo "# POI03| INVENTREE_DB_HOST=${INVENTREE_DB_HOST}" @@ -229,7 +229,7 @@ function detect_envs() { function create_initscripts() { # Make sure python env exists - if test -f "${APP_HOME}/env"; then + if [[ -f "${APP_HOME}/env" ]]; then echo "# POI09| python environment already present - skipping" else echo "# POI09| Setting up python environment" @@ -237,14 +237,14 @@ function create_initscripts() { sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && env/bin/pip install invoke wheel" # Check INSTALLER_EXTRA exists and load it - if test -f "${APP_HOME}/INSTALLER_EXTRA"; then + if [[ -f "${APP_HOME}/INSTALLER_EXTRA" ]]; then echo "# POI09| Loading extra packages from INSTALLER_EXTRA" source ${APP_HOME}/INSTALLER_EXTRA fi - if [ -n "${SETUP_EXTRA_PIP}" ]; then + if [[ -n "${SETUP_EXTRA_PIP}" ]]; then echo "# POI09| Installing extra pip packages" - if [ -n "${SETUP_DEBUG}" ]; then + if [[ -n "${SETUP_DEBUG}" ]]; then echo "# POI09| Extra pip packages: ${SETUP_EXTRA_PIP}" fi sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && env/bin/pip install ${SETUP_EXTRA_PIP}" @@ -254,7 +254,7 @@ function create_initscripts() { fi # Unlink default config if it exists - if test -f "/etc/nginx/sites-enabled/default"; then + if [[ -f "/etc/nginx/sites-enabled/default" ]]; then echo "# POI09| Unlinking default nginx config\n# POI09| Old file still in /etc/nginx/sites-available/default" sudo unlink /etc/nginx/sites-enabled/default echo "# POI09| Unlinked default nginx config" @@ -285,12 +285,12 @@ function create_initscripts() { function create_admin() { # Create data for admin users - stop with setting SETUP_ADMIN_NOCREATION to true - if [ "${SETUP_ADMIN_NOCREATION}" == "true" ]; then + if [[ "${SETUP_ADMIN_NOCREATION}" == "true" ]]; then echo "# POI10| Admin creation is disabled - skipping" return fi - if test -f "${SETUP_ADMIN_PASSWORD_FILE}"; then + if [[ -f "${SETUP_ADMIN_PASSWORD_FILE}" ]]; then echo "# POI10| Admin data already exists - skipping" else echo "# POI10| Creating admin user data" @@ -300,7 +300,7 @@ function create_admin() { export INVENTREE_ADMIN_EMAIL=${INVENTREE_ADMIN_EMAIL:-admin@example.com} # Create password if not set - if [ -z "${INVENTREE_ADMIN_PASSWORD}" ]; then + if [[ -z "${INVENTREE_ADMIN_PASSWORD}" ]]; then openssl rand -base64 32 >${SETUP_ADMIN_PASSWORD_FILE} export INVENTREE_ADMIN_PASSWORD=$(cat ${SETUP_ADMIN_PASSWORD_FILE}) fi @@ -381,15 +381,15 @@ function set_env() { function set_site() { # Ensure IP is known - if [ -z "${INVENTREE_IP}" ]; then + if [[ -z "${INVENTREE_IP}" ]]; then echo "# POI14| No IP address found - skipping" return fi # Check if INVENTREE_SITE_URL in inventree config - if [ -z "$(inventree config:get INVENTREE_SITE_URL)" ]; then + if [[ -z "$(inventree config:get INVENTREE_SITE_URL)" ]]; then # Prefer current INVENTREE_SITE_URL if set - if [ -n "${INVENTREE_SITE_URL}" ]; then + if [[ -n "${INVENTREE_SITE_URL}" ]]; then inventree config:set INVENTREE_SITE_URL=${INVENTREE_SITE_URL} else echo "# POI14| Setting up InvenTree site URL" @@ -407,7 +407,7 @@ function final_message() { echo -e "${SETUP_NGINX_FILE}" echo -e "Try opening InvenTree with any of \n${INVENTREE_SITE_URL} , http://localhost/ or http://${INVENTREE_IP}/ \n" # Print admin user data only if set - if [ -n "${INVENTREE_ADMIN_USER}" ]; then + if [[ -n "${INVENTREE_ADMIN_USER}" ]]; then echo -e "Admin user data:" echo -e " Email: ${INVENTREE_ADMIN_EMAIL}" echo -e " Username: ${INVENTREE_ADMIN_USER}" @@ -434,10 +434,10 @@ function update_checks() { local name=$3 local value=$(inventree config:get ${env_key}) - if [ -z "${value}" ] || [ "$value" == "null" ]; then + if [[ -z "${value}" ]] || [[ "$value" == "null" ]]; then value=$(jq -r ".[].${config_key}" <<< ${INVENTREE_CONF_DATA}) fi - if [ -z "${value}" ] || [ "$value" == "null" ]; then + if [[ -z "${value}" ]] || [[ "$value" == "null" ]]; then echo "# POI08| No setting for ${name} found - please set it manually either in ${INVENTREE_CONFIG_FILE} under '${config_key}' or with 'inventree config:set ${env_key}=value'" ABORT=true else @@ -446,14 +446,14 @@ function update_checks() { } # Custom checks if old version is below 0.8.0 - if [ "${old_version_rev}" -lt "9" ]; then + if [[ "${old_version_rev}" -lt "9" ]]; then echo "# POI08| Old version is below 0.9.0 - You might be missing some configs" # Check for BACKUP_DIR and SITE_URL in INVENTREE_CONF_DATA and config check_config_value "INVENTREE_SITE_URL" "site_url" "site URL" check_config_value "INVENTREE_BACKUP_DIR" "backup_dir" "backup dir" - if [ "${ABORT}" = true ]; then + if [[ "${ABORT}" = true ]]; then echo "# POI08| Aborting - please set the missing values and run the update again" exit 1 fi diff --git a/contrib/packager.io/postinstall.sh b/contrib/packager.io/postinstall.sh index c63c2f3436..ccb3f02ca1 100755 --- a/contrib/packager.io/postinstall.sh +++ b/contrib/packager.io/postinstall.sh @@ -42,7 +42,7 @@ detect_python # Check if we are updating and need to alert echo "# POI08| Checking if update checks are needed" -if [ -z "$2" ]; then +if [[ -z "$2" ]]; then echo "# POI08| Normal install - no need for checks" else update_checks $2 @@ -56,7 +56,7 @@ create_admin stop_inventree update_or_install # Write config file -if [ "${SETUP_CONF_LOADED}" = "true" ]; then +if [[ "${SETUP_CONF_LOADED}" = "true" ]]; then set_env fi set_site diff --git a/contrib/packager.io/preinstall.sh b/contrib/packager.io/preinstall.sh index 9ae899fdb9..1e34612942 100755 --- a/contrib/packager.io/preinstall.sh +++ b/contrib/packager.io/preinstall.sh @@ -8,7 +8,7 @@ PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin: # Envs that should be passed to setup commands export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_BACKUP_DIR,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,INVENTREE_SITE_URL,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP,SETUP_PYTHON -if test -f "${APP_HOME}/env/bin/pip"; then +if [[ -f "${APP_HOME}/env/bin/pip" ]]; then # Check if clear-generated is available if sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && invoke int.clear-generated --help" > /dev/null 2>&1; then echo "# PRI02| Clearing precompiled files" diff --git a/src/backend/InvenTree/InvenTree/config.py b/src/backend/InvenTree/InvenTree/config.py index aa54bb6014..3da990ce2e 100644 --- a/src/backend/InvenTree/InvenTree/config.py +++ b/src/backend/InvenTree/InvenTree/config.py @@ -199,7 +199,7 @@ def load_config_data(set_cache: bool = False) -> map | None: try: data = yaml.safe_load(cfg) except yaml.parser.ParserError as error: - logger.error( + logger.exception( "INVE-E13: Error reading InvenTree configuration file '%s': %s", cfg_file, error, diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 970bcf0363..8d7d02184c 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -2423,9 +2423,7 @@ class OrderCalendarExport(ICalFeed): whether or not to show completed orders. Defaults to false """ - instance_url = get_base_url() - - instance_url = instance_url.replace('http://', '').replace('https://', '') + instance_url = get_base_url().replace('http://', '').replace('https://', '') timezone = settings.TIME_ZONE file_name = 'calendar.ics' diff --git a/src/backend/InvenTree/plugin/base/supplier/mixins.py b/src/backend/InvenTree/plugin/base/supplier/mixins.py index 56e924f061..12e3ec2675 100644 --- a/src/backend/InvenTree/plugin/base/supplier/mixins.py +++ b/src/backend/InvenTree/plugin/base/supplier/mixins.py @@ -15,6 +15,7 @@ from plugin.base.supplier import helpers as supplier from plugin.mixins import SettingsMixin PartData = TypeVar('PartData') +NOT_IMPLEMENTED_ERROR = 'This method needs to be overridden.' class SupplierMixin(SettingsMixin, Generic[PartData]): @@ -50,25 +51,25 @@ class SupplierMixin(SettingsMixin, Generic[PartData]): # --- Methods to be overridden by plugins --- def get_suppliers(self) -> list[supplier.Supplier]: """Return a list of available suppliers.""" - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def get_search_results( self, supplier_slug: str, term: str ) -> list[supplier.SearchResult]: """Return a list of search results for the given search term.""" - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def get_import_data(self, supplier_slug: str, part_id: str) -> PartData: """Return the import data for the given part ID.""" - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def get_pricing_data(self, data: PartData) -> dict[int, tuple[float, str]]: """Return a dictionary of pricing data for the given part data.""" - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def get_parameters(self, data: PartData) -> list[supplier.ImportParameter]: """Return a list of parameters for the given part data.""" - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def import_part( self, @@ -86,7 +87,7 @@ class SupplierMixin(SettingsMixin, Generic[PartData]): - create related parts - add attachments to the part """ - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def import_manufacturer_part( self, data: PartData, *, part: part_models.Part @@ -101,7 +102,7 @@ class SupplierMixin(SettingsMixin, Generic[PartData]): - Adding parameters to the manufacturer part - Adding attachments to the manufacturer part """ - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def import_supplier_part( self, @@ -116,7 +117,7 @@ class SupplierMixin(SettingsMixin, Generic[PartData]): - Creating a new supplier part - Creating supplier price breaks """ - raise NotImplementedError('This method needs to be overridden.') + raise NotImplementedError(NOT_IMPLEMENTED_ERROR) # --- Helper methods for importing parts --- def download_image(self, img_url: str): diff --git a/src/backend/InvenTree/scim/views.py b/src/backend/InvenTree/scim/views.py index f0b0da9322..4804da460c 100644 --- a/src/backend/InvenTree/scim/views.py +++ b/src/backend/InvenTree/scim/views.py @@ -7,7 +7,7 @@ from django.shortcuts import get_object_or_404 import structlog from drf_spectacular.utils import extend_schema -from pydantic import ValidationError +from pydantic import BaseModel, ValidationError from rest_framework.decorators import ( api_view, authentication_classes, @@ -70,9 +70,19 @@ class ScimParser(JSONParser): media_type = 'application/scim+json' -def scim_dump(obj) -> dict: - """Serialize a scim2_models object to a plain (JSON-safe) dict.""" - return obj.model_dump(mode='json', exclude_none=True, by_alias=True) +class ScimResponse(Response): + """Custom Response class for SCIM protocol responses.""" + + def __init__(self, data, *args, **kwargs): + """Like Response but with the scim suff auto-solved.""" + if isinstance(data, DjangoUser): + data = user_to_scim(data) + elif isinstance(data, DjangoGroup): + data = group_to_scim(data) + + if isinstance(data, BaseModel): + data = data.model_dump(mode='json', exclude_none=True, by_alias=True) + super().__init__(data, *args, content_type='application/scim+json', **kwargs) def scim_error(status: int, detail: str, scim_type: str | None = None) -> Response: @@ -83,9 +93,7 @@ def scim_error(status: int, detail: str, scim_type: str | None = None) -> Respon scim_type=scim_type, detail=detail, ) - return Response( - scim_dump(error), status=status, content_type='application/scim+json' - ) + return ScimResponse(error, status=status) class ScimAPIView(APIView): @@ -168,7 +176,7 @@ class ServiceProviderConfigView(ScimAPIView): ) ], ) - return Response(scim_dump(config), content_type='application/scim+json') + return ScimResponse(config) class ResourceTypesView(ScimAPIView): @@ -202,9 +210,7 @@ class ResourceTypesView(ScimAPIView): resource_type = self.resource_types.get(name) if not resource_type: return scim_error(404, f"ResourceType '{name}' not found") - return Response( - scim_dump(resource_type), content_type='application/scim+json' - ) + return ScimResponse(resource_type) resources = list(self.resource_types.values()) response = ListResponse[ResourceType]( @@ -214,7 +220,7 @@ class ResourceTypesView(ScimAPIView): start_index=1, resources=resources, ) - return Response(scim_dump(response), content_type='application/scim+json') + return ScimResponse(response) class SchemasView(ScimAPIView): @@ -233,7 +239,7 @@ class SchemasView(ScimAPIView): schema = schemas.get(schema_id) if not schema: return scim_error(404, f"Schema '{schema_id}' not found") - return Response(scim_dump(schema), content_type='application/scim+json') + return ScimResponse(schema) resources = list(schemas.values()) response = ListResponse[Schema]( @@ -243,7 +249,7 @@ class SchemasView(ScimAPIView): start_index=1, resources=resources, ) - return Response(scim_dump(response), content_type='application/scim+json') + return ScimResponse(response) class BaseResourceView(ScimAPIView): @@ -291,7 +297,7 @@ class BaseResourceView(ScimAPIView): start_index=start_index, resources=resources, ) - return Response(scim_dump(response), content_type='application/scim+json') + return ScimResponse(response) class UsersView(BaseResourceView): @@ -325,11 +331,8 @@ class UsersView(BaseResourceView): logger.info('SCIM: provisioned new user', username=user.username) - return Response( - scim_dump(user_to_scim(user)), - status=201, - content_type='application/scim+json', - headers={'Location': user_location(user.pk)}, + return ScimResponse( + user, status=201, headers={'Location': user_location(user.pk)} ) @@ -343,9 +346,7 @@ class UserDetailView(ScimAPIView): def get(self, request, pk, *args, **kwargs): """Retrieve a single user.""" user = self.get_object(pk) - return Response( - scim_dump(user_to_scim(user)), content_type='application/scim+json' - ) + return ScimResponse(user) def put(self, request, pk, *args, **kwargs): """Replace a user's attributes.""" @@ -353,9 +354,7 @@ class UserDetailView(ScimAPIView): scim_user = User.model_validate(request.data) apply_scim_to_user(scim_user, user) user.save() - return Response( - scim_dump(user_to_scim(user)), content_type='application/scim+json' - ) + return ScimResponse(user) def patch(self, request, pk, *args, **kwargs): """Apply a SCIM PATCH operation set to a user.""" @@ -368,9 +367,7 @@ class UserDetailView(ScimAPIView): apply_scim_to_user(scim_user, user) user.save() - return Response( - scim_dump(user_to_scim(user)), content_type='application/scim+json' - ) + return ScimResponse(user) def delete(self, request, pk, *args, **kwargs): """Deactivate a user. @@ -414,11 +411,8 @@ class GroupsView(BaseResourceView): logger.info('SCIM: provisioned new group', name=group.name) - return Response( - scim_dump(group_to_scim(group)), - status=201, - content_type='application/scim+json', - headers={'Location': group_location(group.pk)}, + return ScimResponse( + group, status=201, headers={'Location': group_location(group.pk)} ) @@ -432,9 +426,7 @@ class GroupDetailView(ScimAPIView): def get(self, request, pk, *args, **kwargs): """Retrieve a single group.""" group = self.get_object(pk) - return Response( - scim_dump(group_to_scim(group)), content_type='application/scim+json' - ) + return ScimResponse(group) def put(self, request, pk, *args, **kwargs): """Replace a group's attributes and membership.""" @@ -443,9 +435,7 @@ class GroupDetailView(ScimAPIView): apply_scim_to_group(scim_group, group) group.save() set_group_members(group, scim_group.members) - return Response( - scim_dump(group_to_scim(group)), content_type='application/scim+json' - ) + return ScimResponse(group) def patch(self, request, pk, *args, **kwargs): """Apply a SCIM PATCH operation set to a group (used mainly for membership changes).""" @@ -459,9 +449,7 @@ class GroupDetailView(ScimAPIView): group.save() set_group_members(group, scim_group.members) - return Response( - scim_dump(group_to_scim(group)), content_type='application/scim+json' - ) + return ScimResponse(group) def delete(self, request, pk, *args, **kwargs): """Remove a group.""" diff --git a/src/frontend/src/pages/part/bom/BomCompare.tsx b/src/frontend/src/pages/part/bom/BomCompare.tsx index 51e3986e77..c1a65b4cea 100644 --- a/src/frontend/src/pages/part/bom/BomCompare.tsx +++ b/src/frontend/src/pages/part/bom/BomCompare.tsx @@ -338,9 +338,9 @@ export function BomCompareDrawer({ // Show only *common* parts rows = rows.filter((row) => row.match); break; - default: case 'all': break; + default: } // Return rows, sorted by part name