From cd0cf9953dff3d6e6ffc25df340341d15aa35b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lipovsk=C3=BD?= Date: Thu, 27 Apr 2023 15:59:12 +0200 Subject: [PATCH] Adding filtering builds based on `from_index` CLOUDDST-18396 --- iib/web/api_v1.py | 67 ++++++++++++++++++++++------------- tests/test_web/test_api_v1.py | 38 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/iib/web/api_v1.py b/iib/web/api_v1.py index abfdb8ce1..fcb45aac1 100644 --- a/iib/web/api_v1.py +++ b/iib/web/api_v1.py @@ -366,7 +366,7 @@ def get_builds() -> flask.Response: request_type = flask.request.args.get('request_type') user = flask.request.args.get('user') index_image = flask.request.args.get('index_image') - + from_index = flask.request.args.get('from_index') query_params = {} # Create an alias class to load the polymorphic classes @@ -397,35 +397,57 @@ def get_builds() -> flask.Response: query_params['user'] = user query = query.join(Request.user).filter(User.username == user) - if index_image: + if index_image or from_index: # https://sqlalche.me/e/20/xaj2 - Create aliases for self-join (Sqlalchemy 2.0) request_create_empty_index_alias = aliased(RequestCreateEmptyIndex, flat=True) request_add_alias = aliased(RequestAdd, flat=True) request_rm_alias = aliased(RequestRm, flat=True) - request_merge_index_image_alias = aliased(RequestMergeIndexImage, flat=True) request_fbc_operations_alias = aliased(RequestFbcOperations, flat=True) - query_params['index_image'] = index_image - # Get the image id of the image to be searched - image_result = Image.query.filter_by(pull_specification=index_image).first() - if image_result: - # join with the Request* tables to get the response as image_ids are stored there - query = ( - query.outerjoin( - request_create_empty_index_alias, - Request.id == request_create_empty_index_alias.id, - ) - .outerjoin(request_add_alias, Request.id == request_add_alias.id) - .outerjoin( - request_merge_index_image_alias, - Request.id == request_merge_index_image_alias.id, - ) - .outerjoin(request_rm_alias, Request.id == request_rm_alias.id) - .outerjoin( - request_fbc_operations_alias, Request.id == request_fbc_operations_alias.id + # join with the Request* tables to get the response as image_ids are stored there + query = ( + query.outerjoin( + request_create_empty_index_alias, + Request.id == request_create_empty_index_alias.id, + ) + .outerjoin(request_add_alias, Request.id == request_add_alias.id) + .outerjoin(request_rm_alias, Request.id == request_rm_alias.id) + .outerjoin(request_fbc_operations_alias, Request.id == request_fbc_operations_alias.id) + ) + + if from_index: + query_params['from_index'] = from_index + # Get the image id of the image to be searched + from_index_result = Image.query.filter_by(pull_specification=from_index).first() + if not from_index_result: + # if from_index is not found in image table, then raise an error + raise ValidationError(f'from_index {from_index} is not a valid index image') + + query = query.filter( + or_( + request_create_empty_index_alias.from_index_id == from_index_result.id, + request_add_alias.from_index_id == from_index_result.id, + request_rm_alias.from_index_id == from_index_result.id, + request_fbc_operations_alias.from_index_id == from_index_result.id, ) ) + if index_image: + # Get the image id of the image to be searched for + image_result = Image.query.filter_by(pull_specification=index_image).first() + if not image_result: + # if index_image is not found in image table, then raise an error + raise ValidationError(f'{index_image} is not a valid index image') + + request_merge_index_image_alias = aliased(RequestMergeIndexImage, flat=True) + query_params['index_image'] = index_image + + # join with the Request* tables to get the response as image_ids are stored there + query = query.outerjoin( + request_merge_index_image_alias, + Request.id == request_merge_index_image_alias.id, + ) + query = query.filter( or_( request_create_empty_index_alias.index_image_id == image_result.id, @@ -435,9 +457,6 @@ def get_builds() -> flask.Response: request_fbc_operations_alias.index_image_id == image_result.id, ) ) - # if index_image is not found in image table, then raise an error - else: - raise ValidationError(f'{index_image} is not a valid index image') pagination_query = query.order_by(Request.id.desc()).paginate(max_per_page=max_per_page) requests = pagination_query.items diff --git a/tests/test_web/test_api_v1.py b/tests/test_web/test_api_v1.py index 839ea8196..c38ac416f 100644 --- a/tests/test_web/test_api_v1.py +++ b/tests/test_web/test_api_v1.py @@ -210,6 +210,44 @@ def test_index_image_filter( assert rv.json == {'error': ('quay.io/namespace/index@sha256:abc is not a valid index image')} +def _request_add_state_and_from_index(minimal_request, image_pull_spec): + minimal_request.add_state('in_progress', 'Starting things up!') + minimal_request.from_index = Image.get_or_create(image_pull_spec) + minimal_request.add_state('complete', 'The request is complete') + + +def test_from_index_filter( + app, client, db, minimal_request_add, minimal_request_rm, minimal_request_fbc_operations +): + _request_add_state_and_from_index( + minimal_request_add, 'quay.io/namespace/index@sha256:from_index' + ) + _request_add_state_and_from_index( + minimal_request_rm, 'quay.io/namespace/from_index@sha256:123456' + ) + _request_add_state_and_from_index( + minimal_request_fbc_operations, + 'quay.io/namespace/from_index@sha256:fbcop', + ) + db.session.commit() + + rv_json = client.get('/api/v1/builds?from_index=quay.io/namespace/index@sha256:from_index').json + assert rv_json['meta']['total'] == 1 + + rv_json = client.get( + '/api/v1/builds?from_index=quay.io/namespace/from_index@sha256:123456' + ).json + assert rv_json['meta']['total'] == 1 + + rv_json = client.get('/api/v1/builds?from_index=quay.io/namespace/from_index@sha256:fbcop').json + assert rv_json['meta']['total'] == 1 + + rv = client.get('/api/v1/builds?from_index=quay.io/namespace/index@sha256:abc') + assert rv.json == { + 'error': 'from_index quay.io/namespace/index@sha256:abc is not a valid index image' + } + + def test_get_builds_invalid_state(app, client, db): rv = client.get('/api/v1/builds?state=is_it_lunch_yet%3F') assert rv.status_code == 400