From 98d06639a4aed669cb395940a0ed9dfd4cdaef4b Mon Sep 17 00:00:00 2001 From: Sebastian Aranda Date: Mon, 16 Dec 2024 18:28:37 -0300 Subject: [PATCH] Add new components_json field to the jira_fields table. Also add deprecation advice and code comments for systems, subsystems, cscs, components, primary_software_components and primary_hardware_components. --- ...3f83_add_components_json_field_to_jira_.py | 42 ++++ src/narrativelog/create_tables.py | 16 +- src/narrativelog/message.py | 41 +++- src/narrativelog/routers/add_message.py | 54 ++++- src/narrativelog/routers/edit_message.py | 52 ++++- src/narrativelog/routers/find_messages.py | 191 ++++++++++++++++-- 6 files changed, 365 insertions(+), 31 deletions(-) create mode 100644 alembic/versions/49ef39173f83_add_components_json_field_to_jira_.py diff --git a/alembic/versions/49ef39173f83_add_components_json_field_to_jira_.py b/alembic/versions/49ef39173f83_add_components_json_field_to_jira_.py new file mode 100644 index 0000000..c2f599d --- /dev/null +++ b/alembic/versions/49ef39173f83_add_components_json_field_to_jira_.py @@ -0,0 +1,42 @@ +"""add components_json field to jira_fields table + +Revision ID: 49ef39173f83 +Revises: 54f755dbdb6f +Create Date: 2024-12-18 17:01:39.676895 + +""" +import logging + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision = "49ef39173f83" +down_revision = "54f755dbdb6f" +branch_labels = None +depends_on = None + + +JIRA_FIELDS_TABLE_NAME = "jira_fields" + + +def upgrade(log: logging.Logger, table_names: set[str]) -> None: + if JIRA_FIELDS_TABLE_NAME not in table_names: + log.info(f"No {JIRA_FIELDS_TABLE_NAME} table; nothing to do") + return + log.info("Add 'components_json'") + + op.add_column( + JIRA_FIELDS_TABLE_NAME, + sa.Column("components_json", sa.JSON(), nullable=True), + ) + + +def downgrade(log: logging.Logger, table_names: set[str]) -> None: + if JIRA_FIELDS_TABLE_NAME not in table_names: + log.info(f"No {JIRA_FIELDS_TABLE_NAME} table; nothing to do") + return + + log.info("Drop 'components_json'") + op.drop_column(JIRA_FIELDS_TABLE_NAME, "components_json") diff --git a/src/narrativelog/create_tables.py b/src/narrativelog/create_tables.py index 9ed05c0..5b95d57 100644 --- a/src/narrativelog/create_tables.py +++ b/src/narrativelog/create_tables.py @@ -8,7 +8,7 @@ import sqlalchemy as sa import sqlalchemy.types as saty -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.dialects.postgresql import JSONB, UUID # Length of the site_id field. SITE_ID_LEN = 16 @@ -60,8 +60,14 @@ def create_message_table(metadata: sa.MetaData) -> sa.Table: sa.Column("date_invalidated", saty.DateTime(), nullable=True), sa.Column("parent_id", UUID(as_uuid=True), nullable=True), # Added 2022-07-19 + # 'systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead sa.Column("systems", saty.ARRAY(sa.Text), nullable=True), + # 'subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead sa.Column("subsystems", saty.ARRAY(sa.Text), nullable=True), + # 'cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead sa.Column("cscs", saty.ARRAY(sa.Text), nullable=True), # Added 2022-07-37 sa.Column("date_end", saty.DateTime(), nullable=True), @@ -110,10 +116,18 @@ def create_jira_fields_table(metadata: sa.MetaData) -> sa.Table: sa.Column( "id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ), + # Added 2024-12-16 + sa.Column("components_json", JSONB, nullable=True), + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead sa.Column("components", saty.ARRAY(sa.Text), nullable=True), + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead sa.Column( "primary_software_components", saty.ARRAY(sa.Text), nullable=True ), + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead sa.Column( "primary_hardware_components", saty.ARRAY(sa.Text), nullable=True ), diff --git a/src/narrativelog/message.py b/src/narrativelog/message.py index d009a4e..ab855b2 100644 --- a/src/narrativelog/message.py +++ b/src/narrativelog/message.py @@ -46,13 +46,21 @@ class Message(BaseModel): ) # Added 2022-07-19 systems: None | list[str] = Field( - title="Zero or more system names.", + title="Zero or more system names. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", + ) + subsystems: None | list[str] = Field( + title="Zero or more subsystem names. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", ) - subsystems: None | list[str] = Field(title="Zero or more subsystem names.") cscs: None | list[str] = Field( title="Zero or more CSCs names. " "Each entry should be in the form 'name' or 'name:index', " - "where 'name' is the SAL component name and 'index' is the SAL index." + "where 'name' is the SAL component name and 'index' is the SAL index. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", ) # Added 2022-07-27 date_end: None | datetime.datetime = Field( @@ -61,15 +69,21 @@ class Message(BaseModel): # Added 2023-08-10 components: None | list[str] = Field( title="Zero or more component names. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", ) primary_software_components: None | list[str] = Field( title="Zero or more primary software component names. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", ) primary_hardware_components: None | list[str] = Field( title="Zero or more primary hardware component names. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "This field is deprecated and will be removed in v1.0.0. " + "Please use 'components_json' instead.", ) # Added 2023-10-24 category: None | str = Field( @@ -78,6 +92,14 @@ class Message(BaseModel): time_lost_type: None | str = Field( title="Type of time lost.", ) + # Added 2024-12-16 + components_json: None | dict = Field( + default_factory=dict, + title="JSON representation of systems and subsystems on the OBS jira project. " + "For a full list of valid keys please refer to: " + "https://rubinobs.atlassian.net/wiki/spaces/LSSTCOM/pages/53741849" + "/Systems+Sub-Systems+and+Components+Proposal+for+JIRA", + ) class Config: orm_mode = True @@ -85,9 +107,16 @@ class Config: JIRA_FIELDS = ( + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead "components", + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_software_components", + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_hardware_components", + "components_json", ) MESSAGE_FIELDS = tuple( set(Message.schema()["properties"].keys()) - set(JIRA_FIELDS) diff --git a/src/narrativelog/routers/add_message.py b/src/narrativelog/routers/add_message.py index 74c41c7..526f110 100644 --- a/src/narrativelog/routers/add_message.py +++ b/src/narrativelog/routers/add_message.py @@ -41,37 +41,57 @@ async def add_message( systems: None | list[str] = fastapi.Body( default=None, - description="Zero or more systems to which the message applies.", + description="Zero or more systems to which the message applies. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), subsystems: None | list[str] = fastapi.Body( default=None, - description="Zero or more subsystems to which the message applies", + description="Zero or more subsystems to which the message applies. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), cscs: None | list[str] = fastapi.Body( default=None, description="Zero or more CSCs to which the message applies. " "Each entry should be in the form 'name' or 'name:index', " - "where 'name' is the SAL component name and 'index' is the SAL index.", + "where 'name' is the SAL component name and 'index' is the SAL index. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), components: None | list[str] = fastapi.Body( default=None, description="Zero or more components to which the message applies. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), primary_software_components: None | list[str] = fastapi.Body( default=None, description="Primary software components to which the message applies. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), primary_hardware_components: None | list[str] = fastapi.Body( default=None, description="Primary hardware components to which the message applies. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", + ), + components_json: None + | dict = fastapi.Body( + default=None, + description="JSON representation of systems and subsystems " + "on the OBS jira project. For a full list of valid keys please refer to: " + "https://rubinobs.atlassian.net/wiki/spaces/LSSTCOM/pages/53741849" + "/Systems+Sub-Systems+and+Components+Proposal+for+JIRA", ), urls: list[str] = fastapi.Body( default=[], @@ -147,8 +167,14 @@ async def add_message( user_agent=user_agent, is_human=is_human, date_added=curr_tai.tai.datetime, + # 'systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead systems=systems, + # 'subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead subsystems=subsystems, + # 'cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead cscs=cscs, category=category, time_lost_type=time_lost_type, @@ -166,17 +192,33 @@ async def add_message( if any( field is not None for field in ( + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead components, + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead primary_software_components, + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead primary_hardware_components, + components_json, ) ): result_jira_fields = await connection.execute( jira_fields_table.insert() .values( + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead components=components, + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. + # Please use 'components_json' instead primary_software_components=primary_software_components, + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. + # Please use 'components_json' instead primary_hardware_components=primary_hardware_components, + components_json=components_json, message_id=row_message.id, ) .returning(sa.literal_column("*")) diff --git a/src/narrativelog/routers/edit_message.py b/src/narrativelog/routers/edit_message.py index a1efe7c..712279f 100644 --- a/src/narrativelog/routers/edit_message.py +++ b/src/narrativelog/routers/edit_message.py @@ -44,13 +44,17 @@ async def edit_message( | list[str] = fastapi.Body( default=None, description="Zero or more systems to which the message applied. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), subsystems: None | list[str] = fastapi.Body( default=None, description="Zero or more subsystems to which the message applies. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), cscs: None | list[str] = fastapi.Body( @@ -58,28 +62,44 @@ async def edit_message( description="Zero or more CSCs to which the message applies. " "Each entry should be in the form 'name' or 'name:index', " "where 'name' is the SAL component name and 'index' is the SAL index. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), components: None | list[str] = fastapi.Body( default=None, description="Zero or more components to which the message applies. " "Each entry should be a valid component name entry on the OBS jira project. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), primary_software_components: None | list[str] = fastapi.Body( default=None, description="Primary software components to which the message applies. " "Each entry should be a valid component name entry on the OBS jira project. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", ), primary_hardware_components: None | list[str] = fastapi.Body( default=None, description="Primary hardware components to which the message applies. " "Each entry should be a valid component name entry on the OBS jira project. " - "If specified, replaces all existing entries.", + "If specified, replaces all existing entries." + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_json' instead.", + ), + components_json: None + | dict = fastapi.Body( + default=None, + description="JSON representation of systems and subsystems " + "on the OBS jira project. For a full list of valid keys please refer to: " + "https://rubinobs.atlassian.net/wiki/spaces/LSSTCOM/pages/53741849" + "/Systems+Sub-Systems+and+Components+Proposal+for+JIRA", ), urls: None | list[str] = fastapi.Body( @@ -143,12 +163,25 @@ async def edit_message( "message_text", "level", "tags", + # 'systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead "systems", + # 'subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead "subsystems", + # 'cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_json' instead "cscs", + # 'components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "components", + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_software_components", + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_hardware_components", + "components_json", "category", "time_lost_type", "urls", @@ -165,9 +198,16 @@ async def edit_message( request_data[name] = value jira_update_params = { + # 'components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "components", + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_software_components", + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_json' instead "primary_hardware_components", + "components_json", } async with state.narrativelog_db.engine.begin() as connection: diff --git a/src/narrativelog/routers/find_messages.py b/src/narrativelog/routers/find_messages.py index e345e88..f61d663 100644 --- a/src/narrativelog/routers/find_messages.py +++ b/src/narrativelog/routers/find_messages.py @@ -3,6 +3,7 @@ import datetime import enum import http +import json import fastapi import sqlalchemy as sa @@ -101,42 +102,54 @@ async def find_messages( default=None, description="System names or fragments of names. All messages " "with a system that matches any of these are included. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_systems: None | list[str] = fastapi.Query( default=None, description="System names or fragments of names. All messages " "with a system that matches any of these are excluded. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), subsystems: None | list[str] = fastapi.Query( default=None, description="Subsystem names or fragments of names. All messages " "with a subsystem that matches any of these are included. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_subsystems: None | list[str] = fastapi.Query( default=None, description="Subsystem names or fragments of names. All messages " "with a subsystem that matches any of these are excluded. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), cscs: None | list[str] = fastapi.Query( default=None, description="CSC names or fragments of CSC names, " "of which at least one must be present. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_cscs: None | list[str] = fastapi.Query( default=None, description="CSC names or fragments of CSC names, " "of which all must be absent. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), components: None | list[str] = fastapi.Query( @@ -144,7 +157,9 @@ async def find_messages( description="Component names or fragments of names. All messages " "with a component that matches any of these are included. " "Repeat the parameter for each value. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_components: None | list[str] = fastapi.Query( @@ -152,7 +167,9 @@ async def find_messages( description="Component names or fragments of names. All messages " "with a component that matches any of these are excluded. " "Repeat the parameter for each value. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), primary_software_components: None | list[str] = fastapi.Query( @@ -160,7 +177,9 @@ async def find_messages( description="Primary software components names or fragments of names. " "All messages with a component that matches any of these are included. " "Repeat the parameter for each value. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_primary_software_components: None | list[str] = fastapi.Query( @@ -168,7 +187,9 @@ async def find_messages( description="Primary software components names or fragments of names. " "All messages with a component that matches any of these are excluded. " "Repeat the parameter for each value. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), primary_hardware_components: None | list[str] = fastapi.Query( @@ -176,14 +197,72 @@ async def find_messages( description="Primary hardware components names or fragments of names. " "All messages with a component that matches any of these are included. " "Repeat the parameter for each value. " - "Each entry should be a valid component name entry on the OBS jira project.", + "Each entry should be a valid component name entry on the OBS jira project. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", ), exclude_primary_hardware_components: None | list[str] = fastapi.Query( default=None, description="Primary hardware components names or fragments of names. " "All messages with a component that matches any of these are excluded. " - "Repeat the parameter for each value.", + "Repeat the parameter for each value. " + "**This field is deprecated and will be removed in v1.0.0**. " + "Please use 'components_path' instead.", + ), + components_path: None + | str = fastapi.Query( + default=None, + description="Components structure in JSON format to include. " + "All messages with a \"components_json\" field that " + "matches at least a key with any of the values specified within " + "it are included. The JSON object represents the current " + "hierarchy of systems, subsystems and components on the OBS Jira project: " + "`{\"systems\": [\"system1\", ..., \"systemN\"], " + "\"subsystems\": [\"subsystem1\", ..., \"subsystemN\"], " + "\"components\": [\"component1\", ..., \"componentN\"]}`. " + "E.g. Setting \"components_path\" to `{\"systems\": [\"AuxTel\", \"Simonyi\"], " + "\"subsystems\": [\"Mount\", \"TMA\"], " + "\"components\": [\"ATMCS CSC\"]}` will match " + "all messages that have \"AuxTel\" OR " + "\"Simonyi\" values under the \"systems\" key " + "OR have \"Mount\" OR \"TMA\" values under the \"subsystems\" key " + "OR have \"ATMCS CSC\" value under the \"components\" key. " + "Note that setting \"components_path\" to `{\"subsystems\": " + "[\"Mount\", \"TMA\"]}` is the same as setting it to " + "`{\"subsystems\": [\"TMA\", \"Mount\"]}` so will end up " + "in the same result. Also setting it to `{\"subsystems\": []}` will " + "include all messages that have at least the \"subsystems\" key defined. " + "Any key with a value that is not a list will be ignored. " + "Furthermore setting \"components_path\" to `{}` will have no effect and " + "an invalid JSON will raise a 400 error.", + ), + exclude_components_path: None + | str = fastapi.Query( + default=None, + description="Components structure in JSON format to exclude. " + "All messages with a \"components_json\" field that " + "matches at least a key with any of the values specified within " + "it are excluded. The JSON object represents the current " + "hierarchy of systems, subsystems and components on the OBS Jira project: " + "`{\"systems\": [\"system1\", ..., \"systemN\"], " + "\"subsystems\": [\"subsystem1\", ..., \"subsystemN\"], " + "\"components\": [\"component1\", ..., \"componentN\"]}`. " + "E.g. Setting \"exclude_components_path\" to `{\"systems\": [\"AuxTel\", " + "\"Simonyi\"], \"subsystems\": [\"Mount\", \"TMA\"], " + "\"components\": [\"ATMCS CSC\"]}` will match " + "all messages that have \"AuxTel\" OR " + "\"Simonyi\" values under the \"systems\" key " + "OR have \"Mount\" OR \"TMA\" values under the \"subsystems\" key " + "OR have \"ATMCS CSC\" value under the \"components\" key. " + "Note that setting \"exclude_components_path\" to `{\"subsystems\": " + "[\"Mount\", \"TMA\"]}` is the same as setting it to " + "`{\"subsystems\": [\"TMA\", \"Mount\"]}` so will end up " + "in the same result. Also setting it to `{\"subsystems\": []}` will " + "include all messages that have at least the \"subsystems\" key defined. " + "Any key with a value that is not a list will be ignored. " + "Furthermore setting \"exclude_components_path\" to `{}` will have no effect " + "and an invalid JSON will raise a 400 error.", ), urls: None | list[str] = fastapi.Query( @@ -299,18 +378,44 @@ async def find_messages( "max_level", "user_ids", "user_agents", + # 'systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "systems", + # 'exclude_systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "exclude_systems", + # 'subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "subsystems", + # 'exclude_subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "exclude_subsystems", + # 'cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "cscs", + # 'exclude_cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "exclude_cscs", + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "components", + # 'exclude_components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "exclude_components", + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "primary_software_components", + # 'exclude_primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_primary_software_components", + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "primary_hardware_components", + # 'exclude_primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_primary_hardware_components", + "components_path", + "exclude_components_path", "tags", "exclude_tags", "urls", @@ -386,8 +491,14 @@ async def find_messages( conditions.append(column == None) # noqa elif key in { "tags", + # 'systems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "systems", + # 'subsystems' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "subsystems", + # 'cscs' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "cscs", "urls", }: @@ -406,16 +517,28 @@ async def find_messages( column = message_table.columns[key] conditions.append(column.op("&&")(value)) elif key in { + # 'components' field is deprecated and will be removed in v1.0.0. + # Please use 'components_path' instead "components", + # 'primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "primary_software_components", + # 'primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "primary_hardware_components", }: column = jira_fields_table.columns[key] conditions.append(column.op("&&")(value)) elif key in { "exclude_tags", + # 'exclude_systems' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_systems", + # 'exclude_subsystems' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_subsystems", + # 'exclude_cscs' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_cscs", }: # Value is a list; field name is the end of the key. @@ -425,13 +548,57 @@ async def find_messages( column = message_table.columns[column_name] conditions.append(sa.sql.not_(column.op("&&")(value))) elif key in { + # 'exclude_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_components", + # 'exclude_primary_software_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_primary_software_components", + # 'exclude_primary_hardware_components' field is deprecated + # and will be removed in v1.0.0. Please use 'components_path' instead "exclude_primary_hardware_components", }: column_name = key[8:] column = jira_fields_table.columns[column_name] conditions.append(sa.sql.not_(column.op("&&")(value))) + elif key in {"components_path"}: + try: + parsed_value = json.loads(value) + except json.JSONDecodeError as error: + raise fastapi.HTTPException( + status_code=http.HTTPStatus.BAD_REQUEST, + detail=f"Invalid JSON in {key}: {error}", + ) + column_name = "components_json" + column = jira_fields_table.columns[column_name] + individual_conditions = [] + for key in parsed_value: + value = parsed_value[key] + if not value or not isinstance(value, list): + continue + for element in value: + path = {key: [element]} + individual_conditions.append(column.contains(path)) + conditions.append(sa.sql.or_(*individual_conditions)) + elif key in {"exclude_components_path"}: + try: + parsed_value = json.loads(value) + except json.JSONDecodeError as error: + raise fastapi.HTTPException( + status_code=http.HTTPStatus.BAD_REQUEST, + detail=f"Invalid JSON in {key}: {error}", + ) + column_name = "components_json" + column = jira_fields_table.columns[column_name] + individual_conditions = [] + for key in parsed_value: + value = parsed_value[key] + if not value or not isinstance(value, list): + continue + for element in value: + path = {key: [element]} + individual_conditions.append(column.contains(path)) + conditions.append(sa.sql.not_(sa.sql.or_(*individual_conditions))) elif key in { "site_ids", "instruments",