Skip to content

Feat/migrate get jobs client methods #1695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

paaragon
Copy link
Collaborator

@paaragon paaragon commented Aug 1, 2025

Summary

This PR adds the new jobs and provider_jobs parameters:

  • status
  • created_after
  • function_name (only for jobs)

Details and comments

I also added docstrings for both methods

@paaragon paaragon requested a review from a team as a code owner August 1, 2025 08:55
Copy link
Collaborator

@korgan00 korgan00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job! this is nice.
Since I was working in the files migration in parallel, naturally we didn't developed same solution. I have some lines to discuss.

If you have some time, take a look to the PR and review it because you probably have some comments for me :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean and neat

Comment on lines +17 to +33
def __init__( # pylint: disable=too-many-positional-arguments
self,
user,
limit: Optional[int],
offset: int = 0,
type_filter: Optional[TypeFilter] = None,
status: Optional[str] = None,
created_after: Optional[datetime] = None,
function_name: Optional[str] = None,
):
self.user = user
self.limit = limit
self.offset = offset
self.type_filter = type_filter
self.status = status
self.created_after = created_after
self.function_name = function_name
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two comments here:

  • To remove the "too many positional arguments" maybe you can use the JobRepository >JobFilter dataclass. I think it will also be more readable.
  • I was migrating the files endpoint and I put the args in the execute instead of the init. I think it is good to have consensus on this. In my opinion, most of this parameters are not a "permanent" state, are more related with the action. Also it simplifies the code, we don't have to set object variables, just use them.

What do you think?

Comment on lines +14 to +20
class ProviderNotFoundException(Exception):
"""Provider not found or access denied."""


class FunctionNotFoundException(Exception):
"""Function not found or access denied."""

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the files migration i created a generic NotFoundException in the domain folder: https://github.com/Qiskit/qiskit-serverless/pull/1696/files#diff-e6da6866795b4c157d9b9e887eb27622f036a9dc512d30e5097a301df1d5001c

Also I created an additional decorator to quickly manage the possible NotFoundExceptions (and other that can be created later) and create the corresponding Response. The only cons is that you have to set the message when you create the exception to inform about the issue.

Comment on lines +148 to +161
try:
jobs, total = GetProviderJobsUseCase(**input_data).execute()
except ProviderNotFoundException:
return Response(
{"message": f"Provider {input_data['provider']} doesn't exist."},
status=status.HTTP_404_NOT_FOUND,
)
except FunctionNotFoundException:
return Response(
{
"message": f"Qiskit Function {input_data['provider']}/{input_data['function_name']} doesn't exist." # pylint: disable=line-too-long
},
status=status.HTTP_404_NOT_FOUND,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the files migration i created a generic NotFoundException in the domain folder: https://github.com/Qiskit/qiskit-serverless/pull/1696/files#diff-e6da6866795b4c157d9b9e887eb27622f036a9dc512d30e5097a301df1d5001c

Also I created an additional decorator to quickly manage the possible NotFoundExceptions (and other that can be created later) and create the corresponding Response. The only cons is that you have to set the message when you create the exception to inform about the issue.

Comment on lines +24 to +64
def serialize_input(request):
"""Parse and validate query parameters from the request."""
user = request.user

limit = parse_positive_int(
request.query_params.get("limit"), settings.REST_FRAMEWORK["PAGE_SIZE"]
)
offset = parse_positive_int(request.query_params.get("offset"), 0)

type_filter = None
type_param = request.query_params.get("filter")
if type_param:
try:
type_filter = TypeFilter(type_param)
except ValueError as exc:
raise ValueError(
f"Invalid type filter. Must be one of: {', '.join([e.value for e in TypeFilter])}"
) from exc

status_filter = request.query_params.get("status")

created_after = None
created_after_param = request.query_params.get("created_after")
if created_after_param:
created_after = parse_datetime(created_after_param)
if created_after is None:
raise ValueError(
"Invalid created_after format. Use ISO 8601 format (e.g., '2024-01-01T00:00:00Z')"
)

function_name = request.query_params.get("function")

return {
"user": user,
"limit": limit,
"offset": offset,
"type_filter": type_filter,
"status": status_filter,
"created_after": created_after,
"function_name": function_name,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit complex to read. Maybe a Serializer class fits better in here.

Comment on lines +25 to +60
def serialize_input(request):
"""
Prepare the input for the end-point with validation
"""
user = request.user

limit = parse_positive_int(
request.query_params.get("limit"), settings.REST_FRAMEWORK["PAGE_SIZE"]
)
offset = parse_positive_int(request.query_params.get("offset"), 0)

status_filter = request.query_params.get("status")

created_after = None
created_after_param = request.query_params.get("created_after")
if created_after_param:
created_after = parse_datetime(created_after_param)
if created_after is None:
raise ValueError(
"Invalid created_after format. Use ISO 8601 format (e.g., '2024-01-01T00:00:00Z')"
)

provider = request.query_params.get("provider")
function_name = request.query_params.get("function")
if not provider or not function_name:
raise ValueError("Qiskit Function title and Provider name are mandatory")

return {
"user": user,
"limit": limit,
"offset": offset,
"status": status_filter,
"created_after": created_after,
"provider": provider,
"function_name": function_name,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, maybe we can try with a Serlializer class

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool

@@ -46,4 +46,4 @@
basename=v1_views.CatalogViewSet.BASE_NAME,
)

urlpatterns = router.urls + RouteRegistry.get()
urlpatterns = RouteRegistry.get() + router.urls
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch

@Tansito Tansito changed the base branch from main to feat/migrate-get-jobs-endpoint August 7, 2025 17:34
@Tansito Tansito changed the base branch from feat/migrate-get-jobs-endpoint to main August 7, 2025 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants