def test_access_component_valid(minimal_record, parent, identity_simple, users): record = RDMRecord.create(minimal_record, parent=parent) component = AccessComponent(RDMRecordService()) component.create(identity_simple, minimal_record, record) assert len(record.parent.access.owners) > 0
def required_external_pids_cmp(): service = RDMRecordService(config=TestServiceConfigRequiredExternalPID, pids_service=PIDsService( config=TestServiceConfigRequiredExternalPID, manager_cls=PIDManager)) c = PIDsComponent(service=service) c.uow = UnitOfWork() return c
def no_pids_cmp(): service = RDMRecordService(config=TestServiceConfigNoPIDs, pids_service=PIDsService( config=TestServiceConfigNoPIDs, manager_cls=PIDManager)) c = PIDsComponent(service=service) c.uow = UnitOfWork() return c
def test_access_component_unknown_owner(minimal_record, parent, identity_simple, users): record = RDMRecord.create(minimal_record, parent=parent) record.parent["access"]["owned_by"] = [{"user": -1337}] component = AccessComponent(RDMRecordService()) # both should work, since 'access.owned_by' is ignored for anybody # other than system processes component.create(identity_simple, minimal_record, record) component.update_draft(identity_simple, minimal_record, record)
def test_access_component_unknown_owner_with_system_process( minimal_record, parent, users): record = RDMRecord.create(minimal_record, parent=parent) record.parent["access"]["owned_by"] = [{"user": -1337}] component = AccessComponent(RDMRecordService()) with pytest.raises(ValidationError): component.create(system_identity, minimal_record, record) with pytest.raises(ValidationError): component.update_draft(system_identity, minimal_record, record)
def test_access_component_set_owner(minimal_record, parent, users): user = users[0] identity = Identity(user.id) identity.provides.add(UserNeed(user.id)) record = RDMRecord.create(minimal_record, parent=parent) record.parent["access"]["owned_by"] = [] component = AccessComponent(RDMRecordService()) component.create(identity, minimal_record, record) assert len(record.access.owners) == 1 assert list(record.access.owners)[0].resolve() == user
def test_edtf_dumper_query(app, db, location, minimal_record, users): """Test edft extension queries.""" identity = Identity(users[0].id) identity.provides.add(UserNeed(users[0].id)) identity.provides.add(SystemRoleNeed('any_user')) identity.provides.add(SystemRoleNeed('authenticated_user')) login_user(users[0], remember=True) date = "2021-01-01" minimal_record["metadata"]["publication_date"] = date minimal_record["metadata"]["dates"] = [{"date": date}] # Create the record service = RDMRecordService( config=app.config.get(RDMRecordService.config_name), ) record = service.create(identity, minimal_record) RDMDraft.index.refresh() # Search for it assert service.search_drafts( identity, {"q": "metadata.publication_date_range:[2020 TO 2021]"}, ).total == 1 assert service.search_drafts( identity, {"q": "metadata.publication_date_range:[2020-12-31 TO 2021-01-02]"}, ).total == 1 assert service.search_drafts( identity, {"q": "metadata.publication_date_range:[2022 TO 2023]"}, ).total == 0
def init_services(self, app): """Initialize services.""" from invenio_rdm_records.services import RDMRecordService service_config = GeoRecordServiceConfig service_config.permission_policy_cls = obj_or_import_string( app.config.get("RECORDS_PERMISSIONS_RECORD_POLICY"), default=GeoRecordPermissionPolicy) self.records_service = RDMRecordService( config=self._filter_record_service_config(app, service_config), files_service=FileService(GeoRecordFilesServiceConfig), draft_files_service=FileService(GeoDraftFilesServiceConfig))
def test_access_component_unknown_grant_subject(minimal_record, parent, identity_simple, users): record = RDMRecord.create(minimal_record, parent=parent) record.parent["access"]["grants"] = [{ "subject": "user", "id": "-1337", "level": "view" }] component = AccessComponent(RDMRecordService()) with pytest.raises(ValidationError): component.create(identity_simple, minimal_record, record) with pytest.raises(ValidationError): component.update_draft(identity_simple, minimal_record, record)
def test_metadata_component(minimal_record, parent, identity_simple, location): """Test the metadata component.""" record = RDMRecord.create(minimal_record, parent=parent) draft = RDMDraft.new_version(record) assert 'publication_date' in record.metadata assert 'title' in record.metadata component = MetadataComponent(RDMRecordService()) component.new_version(identity_simple, draft=draft, record=record) # Make sure publication_date was NOT copied, but that title WAS copied assert 'publication_date' not in draft.metadata assert 'title' in draft.metadata # make sure the reference management is correct assert 'publication_date' in record.metadata
def test_access_component_update_access_via_json(minimal_record, parent, users, identity_simple): next_year = arrow.utcnow().datetime + timedelta(days=+365) restricted_access = { "record": "restricted", "files": "restricted", "embargo": { "until": next_year.strftime("%Y-%m-%d"), "active": True, "reason": "nothing in particular", }, } minimal_record["access"] = restricted_access # create an initially restricted-access record record = RDMRecord.create(minimal_record, parent=parent) component = AccessComponent(RDMRecordService()) component.create(identity_simple, minimal_record, record) prot = record.access.protection assert record.access.embargo is not None assert "embargo" in record["access"] assert prot.record == record["access"]["record"] == "restricted" assert prot.files == record["access"]["files"] == "restricted" # update the record's access via new json data # (instead of via the record's access object) new_data = minimal_record.copy() public_access = { "grants": [], "record": "public", "files": "public", } new_data["access"] = public_access component.create(identity_simple, new_data, record) prot = record.access.protection assert not record.access.embargo assert "embargo" not in record["access"] assert prot.record == record["access"]["record"] == "public" assert prot.files == record["access"]["files"] == "public"
def ui_blueprint(app): """Dynamically registers routes (allows us to rely on config).""" blueprint = Blueprint( 'invenio_app_rdm', __name__, template_folder='templates', static_folder='static', ) service = RDMRecordService() @blueprint.before_app_first_request def init_menu(): """Initialize menu before first request.""" item = current_menu.submenu('main.deposit') item.register('invenio_app_rdm.deposits_user', 'Uploads', order=1) search_url = app.config.get('RDM_RECORDS_UI_SEARCH_URL', '/search') @blueprint.route(search_url) def search(): """Search page.""" return render_template(current_app.config['SEARCH_BASE_TEMPLATE']) @blueprint.route(app.config.get('RDM_RECORDS_UI_NEW_URL', '/uploads/new')) def deposits_create(): """Record creation page.""" forms_config = dict(createUrl=("/api/records"), vocabularies=Vocabularies.dump(), current_locale=str(current_i18n.locale)) return render_template( current_app.config['DEPOSITS_FORMS_BASE_TEMPLATE'], forms_config=forms_config, record=dump_empty(RDMRecordSchema), files=dict(default_preview=None, enabled=True, entries=[], links={}), searchbar_config=dict(searchUrl=search_url)) @blueprint.route( app.config.get('RDM_RECORDS_UI_EDIT_URL', '/uploads/<pid_value>')) def deposits_edit(pid_value): """Deposit edit page.""" links_config = RDMDraftResourceConfig.links_config draft = service.read_draft(id_=pid_value, identity=g.identity, links_config=links_config) files_service = RDMDraftFilesService() files_list = files_service.list_files( id_=pid_value, identity=g.identity, links_config=RDMDraftFilesResourceConfig.links_config) forms_config = dict(apiUrl=f"/api/records/{pid_value}/draft", vocabularies=Vocabularies.dump(), current_locale=str(current_i18n.locale)) # Dereference relations (languages, licenses, etc.) draft._record.relations.dereference() serializer = UIJSONSerializer() record = serializer.serialize_object_to_dict(draft.to_dict()) # TODO: get the `is_published` field when reading the draft from invenio_pidstore.errors import PIDUnregistered try: service.draft_cls.pid.resolve(pid_value, registered_only=True) record["is_published"] = True except PIDUnregistered: record["is_published"] = False return render_template( current_app.config['DEPOSITS_FORMS_BASE_TEMPLATE'], forms_config=forms_config, record=record, files=files_list.to_dict(), searchbar_config=dict(searchUrl=search_url)) @blueprint.route( app.config.get('RDM_RECORDS_UI_SEARCH_USER_URL', '/uploads')) def deposits_user(): """List of user deposits page.""" return render_template(current_app.config['DEPOSITS_UPLOADS_TEMPLATE'], searchbar_config=dict(searchUrl=search_url)) @blueprint.route('/coming-soon') def coming_soon(): """Route to display on soon-to-come features.""" return render_template('invenio_app_rdm/coming_soon_page.html') @blueprint.app_template_filter() def make_files_preview_compatible(files): """Processes a list of RecordFiles to a list of FileObjects. This is needed to make the objects compatible with invenio-previewer. """ file_objects = [] for file in files: file_objects.append( FileObject(obj=files[file].object_version, data={}).dumps()) return file_objects @blueprint.app_template_filter() def select_preview_file(files, default_preview=None): """Get list of files and select one for preview.""" selected = None try: for f in sorted(files or [], key=itemgetter('key')): file_type = splitext(f['key'])[1][1:].lower() if is_previewable(file_type): if selected is None: selected = f elif f['key'] == default_preview: selected = f except KeyError: pass return selected @blueprint.app_template_filter() def to_previewer_files(record): """Get previewer-compatible files list.""" return [ FileObject(obj=f.object_version, data=f.metadata or {}) for f in record.files.values() ] @blueprint.app_template_filter('can_list_files') def can_list_files(record): """Permission check if current user can list files of record. The current_user is used under the hood by flask-principal. Once we move to Single-Page-App approach, we likely want to enforce permissions at the final serialization level (only). """ PermissionPolicy = get_record_permission_policy() return PermissionPolicy(action='read_files', record=record).can() @blueprint.app_template_filter('pid_url') def pid_url(identifier, scheme=None, url_scheme='https'): """Convert persistent identifier into a link.""" if scheme is None: try: scheme = idutils.detect_identifier_schemes(identifier)[0] except IndexError: scheme = None try: if scheme and identifier: return idutils.to_url(identifier, scheme, url_scheme=url_scheme) except Exception: current_app.logger.warning( f"URL generation for identifier {identifier} failed.", exc_info=True) return '' @blueprint.app_template_filter('doi_identifier') def doi_identifier(identifiers): """Extract DOI from sequence of identifiers.""" for identifier in identifiers: # TODO: extract this "DOI" constant to a registry? if identifier == 'doi': return identifiers[identifier] @blueprint.app_template_filter('vocabulary_title') def vocabulary_title(dict_key, vocabulary_key, alt_key=None): """Returns formatted vocabulary-corresponding human-readable string. In some cases the dict needs to be reconstructed. `alt_key` will be the key while `dict_key` will become the value. """ if alt_key: dict_key = {alt_key: dict_key} vocabulary = Vocabularies.get_vocabulary(vocabulary_key) return vocabulary.get_title_by_dict(dict_key) if vocabulary else "" @blueprint.app_template_filter('dereference_record') def dereference_record(record): """Returns the UI serialization of a record.""" record.relations.dereference() return record @blueprint.app_template_filter('serialize_ui') def serialize_ui(record): """Returns the UI serialization of a record.""" serializer = UIJSONSerializer() # We need a dict not a string return serializer.serialize_object_to_dict(record) return blueprint
def not_req_unmanaged_pid_cmp(): service = RDMRecordService(config=TestServiceNReqUnmanConfig()) return ExternalPIDsComponent(service=service)
def req_managed_pid_cmp(): service = RDMRecordService(config=TestServiceReqManConfig()) return ExternalPIDsComponent(service=service)
def test_grant_tokens_dumper_query(app, db, minimal_record, users, identity_simple, location): """Test grant token dumper extension queries.""" id1 = str(users[0].id) id2 = str(users[1].id) grant1 = Grant.from_dict({"subject": "user", "id": id1, "level": "view"}) grant2 = Grant.from_dict({"subject": "user", "id": id2, "level": "manage"}) grant3 = Grant.from_dict({ "subject": "user", "id": id1, "level": "viewfull" }) minimal_record["access"]["grants"] = [ grant1.to_dict(), grant2.to_dict(), ] # Create the record service = RDMRecordService(config=app.config.get( RDMRecordService.config_name), ) service.create(identity_simple, minimal_record) minimal_record["access"]["grants"] = [ grant2.to_dict(), ] service.create(identity_simple, minimal_record) RDMDraft.index.refresh() # Search for it assert (service.search( identity_simple, { "q": "access.grant_tokens:{}".format(grant1.to_token()) }, status="draft", ).total == 1) assert (service.search( identity_simple, { "q": "access.grant_tokens:{}".format(grant2.to_token()) }, status="draft", ).total == 2) assert (service.search( identity_simple, { "q": "access.grant_tokens:{}".format(grant3.to_token()) }, status="draft", ).total == 0) assert (service.search( identity_simple, { "q": "access.grant_tokens:actually.invalid.token" }, status="draft", ).total == 0)
def no_required_pids_service(): return RDMRecordService(config=TestServiceConfigNoRequiredPIDs, pids_service=PIDsService( config=TestServiceConfigNoRequiredPIDs, manager_cls=PIDManager))