diff --git a/lib/rucio/api/did.py b/lib/rucio/api/did.py index 39b5fb9d67..cd3668f040 100644 --- a/lib/rucio/api/did.py +++ b/lib/rucio/api/did.py @@ -21,7 +21,7 @@ from rucio.common.exception import RucioException from rucio.common.schema import validate_schema from rucio.common.types import InternalAccount, InternalScope -from rucio.common.utils import api_update_return_dict +from rucio.common.utils import api_update_return_dict, extract_scope_bulk from rucio.core import did, naming_convention, meta as meta_core from rucio.core.rse import get_rse_id from rucio.db.sqla.constants import DIDType @@ -724,3 +724,16 @@ def remove_dids_from_followed(dids, account, issuer, *, session: "Session", vo=' account = InternalAccount(account, vo=vo) return did.remove_dids_from_followed(dids=dids, account=account, session=session) + +@read_session +def extract_scope_bulk(dids: list, scopes: Optional[list] = None): + """ + Gets a list with dcitionary containing scope and did for all the dids. + + :param dids: The list of dids to extract scope of. + :param scopes: The list of scopes. + + :returns: a list with dictionary containing scope and did. [{"scope", "", "did",""}] + """ + result = extract_scope_bulk(dids, scopes=scopes) + return result diff --git a/lib/rucio/common/utils.py b/lib/rucio/common/utils.py index c2647dab90..a6d18f4574 100644 --- a/lib/rucio/common/utils.py +++ b/lib/rucio/common/utils.py @@ -38,7 +38,7 @@ from functools import partial, wraps from io import StringIO from itertools import zip_longest -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from urllib.parse import urlparse, urlencode, quote, parse_qsl, urlunparse from uuid import uuid4 as uuid from xml.etree import ElementTree @@ -804,6 +804,12 @@ def extract_scope(did, scopes=None, default_extract=_DEFAULT_EXTRACT): extract_scope_convention = default_extract return _EXTRACT_SCOPE_ALGORITHMS[extract_scope_convention](did=did, scopes=scopes) +def extract_scope_bulk(dids: list, scopes: Optional[list] = None, default_extract: str = _DEFAULT_EXTRACT): + results = [] + for did in dids: + scope, did = extract_scope(did, scopes=scopes, default_extract=default_extract) + results.append({"scope": scope, "did": did}) + return results def pid_exists(pid): """ diff --git a/lib/rucio/web/rest/flaskapi/v1/dids.py b/lib/rucio/web/rest/flaskapi/v1/dids.py index 4c1594752a..5c79ba1897 100644 --- a/lib/rucio/web/rest/flaskapi/v1/dids.py +++ b/lib/rucio/web/rest/flaskapi/v1/dids.py @@ -22,12 +22,12 @@ list_files, scope_list, get_did, set_metadata, get_metadata, get_metadata_bulk, set_status, attach_dids, \ detach_dids, attach_dids_to_dids, get_dataset_by_guid, list_parent_dids, create_did_sample, list_new_dids, \ resurrect, get_users_following_did, remove_did_from_followed, add_did_to_followed, delete_metadata, \ - set_metadata_bulk, set_dids_metadata_bulk + set_metadata_bulk, set_dids_metadata_bulk, extract_scope_bulk from rucio.api.rule import list_replication_rules, list_associated_replication_rules_for_file from rucio.common.exception import ScopeNotFound, DatabaseException, DataIdentifierNotFound, DataIdentifierAlreadyExists, \ DuplicateContent, AccessDenied, KeyNotFound, Duplicate, InvalidValueForKey, UnsupportedStatus, \ UnsupportedOperation, RSENotFound, RuleNotFound, InvalidMetadata, InvalidPath, FileAlreadyExists, InvalidObject, FileConsistencyMismatch -from rucio.common.utils import render_json, APIEncoder +from rucio.common.utils import render_json, APIEncoder, render_json_list from rucio.db.sqla.constants import DIDType from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint from rucio.web.rest.flaskapi.v1.common import response_headers, check_accept_header_wrapper_flask, \ @@ -115,6 +115,68 @@ def generate(name, recursive, vo): except DataIdentifierNotFound as error: return generate_http_error_flask(404, error) +class ExactScopeBulk(ErrorHandlingMethodView): + + @check_accept_header_wrapper_flask(['application/json']) + def post(self): + """ + --- + summary: extract scope. + description: extract scope + tags: + - Data Identifiers + requestBody: + content: + 'application/x-json-stream': + schema: + type: object + required: + - dids + properties: + dids: + description: The dids. + type: array + items: + description: A did. + type: string + scopes: + description: The scopes list. + type: array + items: + description: A scope. + type: string + default: None + properties: + name: + description: The name of the did. + type: string + scope: + description: The scope of the did. + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + description: A list of dictionaries containing scope and did. + type: array + items: + type: object + description: list wih scope and did dict. + 400: + description: Cannot decode json parameter list + """ + parameters = json_parameters() + dids = param_get(parameters, 'dids') + scopes = param_get(parameters, 'scopes', default=None) + try: + result = extract_scope_bulk(dids, scopes=scopes) + return Response(render_json_list(result), 200, content_type='application/json') + except ValueError as error: + return generate_http_error_flask(400, error, 'Cannot decode json parameter list') + except Exception as error: + return generate_http_error_flask(500, error) class Search(ErrorHandlingMethodView): @@ -2154,6 +2216,8 @@ def blueprint(): scope_view = Scope.as_view('scope') bp.add_url_rule('//', view_func=scope_view, methods=['get', ]) + scope_view = ExactScopeBulk.as_view('extract_scope') + #bp.add_url_rule('//', view_func=scope_view, methods=['get', ]) guid_lookup_view = GUIDLookup.as_view('guid_lookup') bp.add_url_rule('//guid', view_func=guid_lookup_view, methods=['get', ]) search_view = Search.as_view('search') diff --git a/lib/rucio/web/rest/flaskapi/v1/scopes.py b/lib/rucio/web/rest/flaskapi/v1/scopes.py index 09701bc4fc..551f8abc43 100644 --- a/lib/rucio/web/rest/flaskapi/v1/scopes.py +++ b/lib/rucio/web/rest/flaskapi/v1/scopes.py @@ -15,7 +15,7 @@ from flask import Flask, request, jsonify -from rucio.api.scope import add_scope, list_scopes, get_scopes +from rucio.api.scope import add_scope, list_scopes, get_scopes, extract_scope_bulk from rucio.common.exception import AccountNotFound, Duplicate, ScopeNotFound from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint from rucio.web.rest.flaskapi.v1.common import check_accept_header_wrapper_flask, response_headers, \ @@ -94,7 +94,6 @@ def post(self, account, scope): return 'Created', 201 - class AccountScopeList(ErrorHandlingMethodView): @check_accept_header_wrapper_flask(['application/json'])