]> git.ipfire.org Git - pbs.git/commitdiff
builds: Give watchers an ID and export over the API
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 15 Jul 2025 11:49:09 +0000 (11:49 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 15 Jul 2025 11:49:09 +0000 (11:49 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/api/builds.py
src/buildservice/builds.py
src/database.sql

index dd7a265a7df28232a262105ed4b0686df388a674..3caaec0026d6dff165041b5aaa43e83ced9f7ced 100644 (file)
@@ -29,7 +29,7 @@ from . import backend
 
 # Import types
 from ..bugtracker import Bug
-from ..builds import Build
+from ..builds import Build, BuildWatcher
 from ..users import User
 
 # Import errors
@@ -120,6 +120,49 @@ async def post(
 async def get(build: Build = fastapi.Depends(get_build_by_uuid)) -> Build:
        return build
 
+#
+# Watchers
+#
+
+@router.get("/{uuid}/watchers")
+async def get_watchers(
+       build: Build = fastapi.Depends(get_build_by_uuid),
+) -> list[BuildWatcher]:
+       """
+               Returns all watchers of a build
+       """
+       return await build.get_watchers()
+
+@router.post("/{uuid}/watchers")
+async def add_watcher(
+       build: Build = fastapi.Depends(get_build_by_uuid),
+       current_user: User = fastapi.Depends(auth.get_current_user),
+) -> fastapi.Response:
+       """
+               Adds yourself as a watcher to the build
+       """
+       # Add the current user as a watcher
+       async with backend.db as session:
+               build.add_watcher(current_user)
+
+       # Return 201
+       return fastapi.Response(fastapi.status.HTTP_201_CREATED)
+
+@router.delete("/{uuid}/watchers")
+async def remove_watcher(
+       build: Build = fastapi.Depends(get_build_by_uuid),
+       current_user: User = fastapi.Depends(auth.get_current_user),
+) -> fastapi.Response:
+       """
+               Removes yourself as a watcher from the build
+       """
+       # Remove the current user as a watcher
+       async with backend.db as session:
+               build.remove_watcher(current_user)
+
+       # Return 204
+       return fastapi.Response(fastapi.status.HTTP_204_NO_CONTENT)
+
 #
 # Bugs
 #
index bb37e81dd5d997af7fdd8367cfa7a0051cf10681..d79b9c56c91eed2c4c0b335d77c336d676a4fded 100644 (file)
@@ -6,6 +6,7 @@ import functools
 import itertools
 import logging
 import os
+import pydantic
 import re
 import sqlalchemy
 import sqlmodel
@@ -688,7 +689,7 @@ class Build(sqlmodel.SQLModel, database.BackendMixin, database.SoftDeleteMixin,
                        # Select all build watchers
                        .select(BuildWatcher)
                        .where(
-                               BuildWatcher.deleted_at == None,
+                               BuildWatcher.removed_at == None,
                                BuildWatcher.build == self,
                        )
                )
@@ -707,7 +708,7 @@ class Build(sqlmodel.SQLModel, database.BackendMixin, database.SoftDeleteMixin,
                        .where(
                                BuildWatcher.build == self,
                                BuildWatcher.user == user,
-                               BuildWatcher.deleted_at == None,
+                               BuildWatcher.removed_at == None,
                        )
                )
 
@@ -736,9 +737,9 @@ class Build(sqlmodel.SQLModel, database.BackendMixin, database.SoftDeleteMixin,
                """
                watcher = await self.watched_by(user)
 
-               # If we have found a watcher, we will delete it
+               # If we have found a watcher, we will remove it
                if watcher:
-                       await watcher.delete()
+                       await watcher.remove()
 
        async def _add_watchers(self):
                """
@@ -1653,7 +1654,7 @@ class BuildPoint(sqlmodel.SQLModel, database.BackendMixin, table=True):
        user: "User" = sqlmodel.Relationship()
 
 
-class BuildWatcher(sqlmodel.SQLModel, database.BackendMixin, database.SoftDeleteMixin, table=True):
+class BuildWatcher(sqlmodel.SQLModel, database.BackendMixin, table=True):
        __tablename__ = "build_watchers"
 
        def __lt__(self, other):
@@ -1662,6 +1663,9 @@ class BuildWatcher(sqlmodel.SQLModel, database.BackendMixin, database.SoftDelete
 
                return NotImplemented
 
+       def __hash__(self):
+               return hash(self.id)
+
        # ID
 
        id: int = sqlmodel.Field(primary_key=True, exclude=True)
@@ -1680,10 +1684,29 @@ class BuildWatcher(sqlmodel.SQLModel, database.BackendMixin, database.SoftDelete
 
        # User
 
-       user: "User" = sqlmodel.Relationship()
+       user: "User" = sqlmodel.Relationship(
+               sa_relationship_kwargs={ "lazy" : "joined", "innerjoin" : True },
+       )
+
+       # Username
+
+       @pydantic.computed_field
+       @property
+       def username(self) -> str:
+               return self.user.name
 
        # Added At
 
        added_at: datetime.datetime = sqlmodel.Field(
                sa_column_kwargs = {"server_default" : sqlalchemy.text("CURRENT_TIMESTAMP")}
        )
+
+       # Removed At
+
+       removed_at: datetime.datetime = sqlmodel.Field(exclude=True)
+
+       async def remove(self):
+               """
+                       Removes the current watcher
+               """
+               self.removed_at = sqlalchemy.func.current_timestamp()
index 96331870e6031dad6be603b3a470d19de35bd94d..6f17bf7757af423c5c650fc3e3732a76083fe6f0 100644 (file)
@@ -143,13 +143,33 @@ CREATE TABLE public.build_points (
 --
 
 CREATE TABLE public.build_watchers (
+    id bigint NOT NULL,
     build_id integer NOT NULL,
     user_id integer NOT NULL,
     added_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
-    deleted_at timestamp without time zone
+    removed_at timestamp without time zone
 );
 
 
+--
+-- Name: build_watchers_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.build_watchers_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+--
+-- Name: build_watchers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.build_watchers_id_seq OWNED BY public.build_watchers.id;
+
+
 --
 -- Name: builder_stats; Type: TABLE; Schema: public; Owner: -
 --
@@ -1200,6 +1220,13 @@ ALTER TABLE ONLY public.build_comments ALTER COLUMN id SET DEFAULT nextval('publ
 ALTER TABLE ONLY public.build_groups ALTER COLUMN id SET DEFAULT nextval('public.build_groups_id_seq'::regclass);
 
 
+--
+-- Name: build_watchers id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.build_watchers ALTER COLUMN id SET DEFAULT nextval('public.build_watchers_id_seq'::regclass);
+
+
 --
 -- Name: builders id; Type: DEFAULT; Schema: public; Owner: -
 --
@@ -1378,6 +1405,14 @@ ALTER TABLE ONLY public.build_groups
     ADD CONSTRAINT build_groups_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: build_watchers build_watchers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.build_watchers
+    ADD CONSTRAINT build_watchers_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: builds builds_pkey; Type: CONSTRAINT; Schema: public; Owner: -
 --
@@ -1610,7 +1645,7 @@ CREATE INDEX build_points_build_id ON public.build_points USING btree (build_id)
 -- Name: build_watchers_unique; Type: INDEX; Schema: public; Owner: -
 --
 
-CREATE UNIQUE INDEX build_watchers_unique ON public.build_watchers USING btree (build_id, user_id) WHERE (deleted_at IS NULL);
+CREATE UNIQUE INDEX build_watchers_unique ON public.build_watchers USING btree (build_id, user_id) WHERE (removed_at IS NULL);
 
 
 --