Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""add order to function job collections
Revision ID: 06596ce2bc3e
Revises: 9dddb16914a4
Create Date: 2025-10-08 13:54:39.943703+00:00
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "06596ce2bc3e"
down_revision = "9dddb16914a4"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"funcapi_function_job_collections_to_function_jobs",
sa.Column("order", sa.Integer(), default=0, nullable=True),
)
# Set the "order" column so that it restarts from 1 for each collection_id,
# ordering by function_job_id within each collection
op.execute(
sa.text(
"""
UPDATE funcapi_function_job_collections_to_function_jobs
SET "order" = sub.row_number
FROM (
SELECT function_job_collection_uuid, function_job_uuid,
ROW_NUMBER() OVER (PARTITION BY function_job_collection_uuid) AS row_number
FROM funcapi_function_job_collections_to_function_jobs
) AS sub
WHERE funcapi_function_job_collections_to_function_jobs.function_job_collection_uuid = sub.function_job_collection_uuid
AND funcapi_function_job_collections_to_function_jobs.function_job_uuid = sub.function_job_uuid
"""
)
)
op.drop_constraint(
"funcapi_function_job_collections_to_function_jobs_pk",
"funcapi_function_job_collections_to_function_jobs",
type_="primary",
)
op.create_primary_key(
"funcapi_function_job_collections_to_function_jobs_pk",
"funcapi_function_job_collections_to_function_jobs",
["function_job_collection_uuid", "order"],
)
op.alter_column(
"funcapi_function_job_collections_to_function_jobs",
"order",
existing_type=sa.Integer(),
nullable=False,
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(
"funcapi_function_job_collections_to_function_jobs_pk",
"funcapi_function_job_collections_to_function_jobs",
type_="primary",
)
op.create_primary_key(
"funcapi_function_job_collections_to_function_jobs_pk",
"funcapi_function_job_collections_to_function_jobs",
["function_job_collection_uuid", "function_job_uuid"],
)

op.drop_column("funcapi_function_job_collections_to_function_jobs", "order")
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,18 @@
nullable=False,
doc="Unique identifier of the function job",
),
sa.Column(
"order",
sa.Integer,
nullable=False,
doc="Order of the function job in the collection",
),
column_created_datetime(),
column_modified_datetime(),
sa.PrimaryKeyConstraint(
"function_job_collection_uuid",
"function_job_uuid",
"order",
name="funcapi_function_job_collections_to_function_jobs_pk",
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ async def create_function_job_collection(
row
)
job_collection_entries: list[Row] = []
for job_id in job_ids:
for order, job_id in enumerate(job_ids, 1):
result = await transaction.execute(
function_job_collections_to_function_jobs_table.insert()
.values(
function_job_collection_uuid=function_job_collection_db.uuid,
function_job_uuid=job_id,
order=order,
)
.returning(
function_job_collections_to_function_jobs_table.c.function_job_collection_uuid,
Expand Down Expand Up @@ -228,10 +229,12 @@ async def list_function_job_collections(
job_ids = [
job_row.function_job_uuid
async for job_row in await conn.stream(
function_job_collections_to_function_jobs_table.select().where(
function_job_collections_to_function_jobs_table.select()
.where(
function_job_collections_to_function_jobs_table.c.function_job_collection_uuid
== row.uuid
)
.order_by(function_job_collections_to_function_jobs_table.c.order)
)
]
collections.append((collection, job_ids))
Expand Down Expand Up @@ -278,10 +281,12 @@ async def get_function_job_collection(
job_ids = [
job_row.function_job_uuid
async for job_row in await conn.stream(
function_job_collections_to_function_jobs_table.select().where(
function_job_collections_to_function_jobs_table.select()
.where(
function_job_collections_to_function_jobs_table.c.function_job_collection_uuid
== row.uuid
)
.order_by(function_job_collections_to_function_jobs_table.c.order)
)
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ async def list_function_jobs_with_status(
function_job_collections_to_function_jobs_table.c.function_job_collection_uuid
== filter_by_function_job_collection_id
)
.order_by(function_job_collections_to_function_jobs_table.c.order)
)
filter_conditions = sqlalchemy.and_(
filter_conditions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,69 @@ async def test_function_job_collection(
)


@pytest.mark.parametrize(
"user_role",
[UserRole.USER],
)
async def test_create_function_job_collection_same_function_job_uuid(
client: TestClient,
add_user_function_api_access_rights: None,
create_fake_function_obj: Callable[[FunctionClass], Function],
webserver_rpc_client: WebServerRpcClient,
logged_user: UserInfoDict,
other_logged_user: UserInfoDict,
user_without_function_api_access_rights: UserInfoDict,
osparc_product_name: ProductName,
):
# Register the function first
registered_function = await webserver_rpc_client.functions.register_function(
function=create_fake_function_obj(FunctionClass.PROJECT),
user_id=logged_user["id"],
product_name=osparc_product_name,
)
assert registered_function.uid is not None

registered_function_job = ProjectFunctionJob(
function_uid=registered_function.uid,
title="Test Function Job",
description="A test function job",
project_job_id=uuid4(),
inputs={"input1": "value1"},
outputs={"output1": "result1"},
job_creation_task_id=None,
)
# Register the function job
function_job_ids = []
registered_function_job = ProjectFunctionJob(
function_uid=registered_function.uid,
title="Test Function Job",
description="A test function job",
project_job_id=uuid4(),
inputs={"input1": "value1"},
outputs={"output1": "result1"},
job_creation_task_id=None,
)
# Register the function job
registered_job = await webserver_rpc_client.functions.register_function_job(
function_job=registered_function_job,
user_id=logged_user["id"],
product_name=osparc_product_name,
)
assert registered_job.uid is not None

function_job_ids = [registered_job.uid] * 3

function_job_collection = FunctionJobCollection(
title="Test Function Job Collection",
description="A test function job collection",
job_ids=function_job_ids,
)

assert function_job_collection.job_ids[0] == registered_job.uid
assert function_job_collection.job_ids[1] == registered_job.uid
assert function_job_collection.job_ids[2] == registered_job.uid


@pytest.mark.parametrize(
"user_role",
[UserRole.USER],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,14 @@ async def test_list_function_jobs_filtering(
)
)

job_ids = [
job.uid
for job in first_registered_function_jobs[1:2]
+ second_registered_function_jobs[0:1]
]
function_job_collection = (
await webserver_rpc_client.functions.register_function_job_collection(
function_job_collection=FunctionJobCollection(
job_ids=[
job.uid
for job in first_registered_function_jobs[1:2]
+ second_registered_function_jobs[0:1]
]
),
function_job_collection=FunctionJobCollection(job_ids=job_ids),
user_id=logged_user["id"],
product_name=osparc_product_name,
)
Expand Down Expand Up @@ -381,6 +380,7 @@ async def test_list_function_jobs_filtering(

# Assert the list contains the registered job
assert len(jobs) == 2
assert [job.uid for job in jobs] == job_ids
assert jobs[0].uid == first_registered_function_jobs[1].uid
assert jobs[1].uid == second_registered_function_jobs[0].uid

Expand Down
Loading