def test_validate_with_format(app, db): """Test that validation can accept custom format rules.""" with app.app_context(): checker = FormatChecker() checker.checks("foo")(lambda el: el.startswith("foo")) data = {"bar": "foo", "$schema": {"properties": {"bar": {"format": "foo"}}}} # test record creation with valid data assert data == Record.create(data) record = Record.create(data, format_checker=checker) # test direct call to validate with valid data assert record.validate(format_checker=checker) is None # test commit with valid data record.commit(format_checker=checker) record["bar"] = "bar" # test direct call to validate with invalid data with pytest.raises(ValidationError) as excinfo: record.validate(format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value) # test commit with invalid data with pytest.raises(ValidationError) as excinfo: record.commit(format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value) data["bar"] = "bar" # test record creation with invalid data with pytest.raises(ValidationError) as excinfo: record = Record.create(data, format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value)
def test_validate_partial(app, db): """Test partial validation.""" schema = {"properties": {"a": {"type": "string"}, "b": {"type": "string"}}, "required": ["b"]} data = {"a": "hello", "$schema": schema} with app.app_context(): # Test validation on create() # normal validation should fail because 'b' is required with pytest.raises(ValidationError) as exc_info: Record.create(data) assert "'b' is a required property" == exc_info.value.message # validate with a less restrictive validator record = Record.create(data, validator=PartialDraft4Validator) # set wrong data types should fails in any case data_incorrect = copy.deepcopy(data) data_incorrect["a"] = 1 with pytest.raises(ValidationError) as exc_info: Record.create(data_incorrect, validator=PartialDraft4Validator) assert "1 is not of type 'string'" == exc_info.value.message # Test validation on commit() # validation not passing with normal validator with pytest.raises(ValidationError) as exc_info: record.commit() assert "'b' is a required property" == exc_info.value.message # validation passing with less restrictive validator assert data == record.commit(validator=PartialDraft4Validator) # set wrong data types should fails in any case record["a"] = 1 with pytest.raises(ValidationError) as exc_info: record.commit(validator=PartialDraft4Validator)
def records(db): """Create sample records with references.""" return [ Record.create({'$ref': 'ref1'}), Record.create({'$ref': 'ref2'}), Record.create({'reflist': [ {'$ref': 'ref3'}, {'$ref': 'ref4'} ]}) ]
def test_file_permissions(app, db, test_object, # fixtures user, access_right, expected): """Test file permissions.""" # Create test users admin = User(email='*****@*****.**', password='******') owner = User(email='*****@*****.**', password='******') auth = User(email='*****@*****.**', password='******') db.session.add_all([admin, owner, auth]) db.session.add( ActionUsers.allow(ActionNeed('admin-access'), user=admin) ) # Create test record rec_uuid = uuid.uuid4() PersistentIdentifier.create( 'recid', '1', object_type='rec', object_uuid=rec_uuid, status=PIDStatus.REGISTERED ) Record.create({ 'recid': 1, 'owners': [2], 'access_right': access_right, '_files': [ { 'key': test_object.key, 'bucket': str(test_object.bucket_id), 'checksum': 'invalid' }, ] }, id_=rec_uuid) db.session.add( RecordsBuckets(record_id=rec_uuid, bucket_id=test_object.bucket_id) ) file_url = url_for( 'invenio_records_ui.recid_files', pid_value='1', filename=test_object.key ) db.session.commit() with app.test_client() as client: if user: # Login as user with client.session_transaction() as sess: sess['user_id'] = User.query.filter_by( email='{}@zenodo.org'.format(user)).one().id sess['_fresh'] = True res = client.get(file_url) assert res.status_code == expected
def files(): """Load files.""" data_path = os.path.join(os.path.dirname(__file__), 'data') # Create location loc = Location(name='local', uri=data_path, default=True) db.session.commit() # Bucket bucket = Bucket.create(loc) # Example files from the data folder example_files = ( 'markdown.md', 'csvfile.csv', 'zipfile.zip', 'jsonfile.json', 'xmlfile.xml', 'notebook.ipynb', 'jpgfile.jpg', 'pngfile.png', ) # Create single file records for f in example_files: with open(os.path.join(data_path, f), 'rb') as fp: create_object(bucket, f, fp) # Create a multi-file record rec_uuid = uuid4() provider = RecordIdProvider.create(object_type='rec', object_uuid=rec_uuid) data = { 'pid_value': provider.pid.pid_value, 'files': [] } # Template to create different files template_file = { 'uri': '/files/{0}/{1}', 'key': '', 'bucket': str(bucket.id), 'local': True } for filename in example_files: file_data = template_file.copy() file_data['uri'] = file_data['uri'].format(str(bucket.id), filename) file_data['key'] = filename data['files'].append(file_data) Record.create(data, id_=rec_uuid) db.session.commit()
def files(): """Load files.""" data_path = os.path.join(os.path.dirname(__file__), 'data') # Create location loc = Location(name='local', uri=data_path, default=True) db.session.commit() # Bucket bucket = Bucket.create(loc) # Example files from the data folder example_files = ( 'markdown.md', 'csvfile.csv', 'zipfile.zip', 'jsonfile.json', 'xmlfile.xml', 'notebook.ipynb', 'jpgfile.jpg', 'pngfile.png', ) # Create single file records for f in example_files: with open(os.path.join(data_path, f), 'rb') as fp: create_object(bucket, f, fp) # Create a multi-file record rec_uuid = uuid4() provider = RecordIdProvider.create(object_type='rec', object_uuid=rec_uuid) data = {'pid_value': provider.pid.pid_value, 'files': []} # Template to create different files template_file = { 'uri': '/files/{0}/{1}', 'key': '', 'bucket': str(bucket.id), 'local': True } for filename in example_files: file_data = template_file.copy() file_data['uri'] = file_data['uri'].format(str(bucket.id), filename) file_data['key'] = filename data['files'].append(file_data) Record.create(data, id_=rec_uuid) db.session.commit()
def test_record_dump(app, db): """Test record dump method.""" with app.app_context(): record = Record.create({'foo': {'bar': 'Bazz', }, }) record_dump = record.dumps() record_dump['foo']['bar'] = 'Spam' assert record_dump['foo']['bar'] != record['foo']['bar']
def record_upsert(json): """Insert or update a record.""" control_number = json.get('control_number', json.get('recid')) if control_number: control_number = int(control_number) pid_type = InspireRecordIdProvider.schema_to_pid_type(json['$schema']) try: pid = PersistentIdentifier.get(pid_type, control_number) record = Record.get_record(pid.object_uuid) record.update(json) record.commit() except PIDDoesNotExistError: record = Record.create(json, id_=None) # Create persistent identifier. inspire_recid_minter(str(record.id), json) if json.get('deleted'): new_recid = get_recid_from_ref(json.get('new_record')) if new_recid: merged_record = get_db_record(pid_type, new_recid) merge_pidstores_of_two_merged_records(merged_record.id, record.id) else: soft_delete_pidstore_for_record(record.id) return record
def migrate_chunk(chunk, broken_output=None, dry_run=False): from invenio_indexer.api import RecordIndexer from ..pidstore.minters import inspire_recid_minter indexer = RecordIndexer() index_queue = [] for raw_record in chunk: record = marc_create_record(raw_record, keep_singletons=False) json_record = create_record(record) if '$schema' in json_record: json_record['$schema'] = url_for( 'invenio_jsonschemas.get_schema', schema_path="records/{0}".format(json_record['$schema']) ) rec_uuid = str(Record.create(json_record, id_=None).id) # Create persistent identifier. pid = inspire_recid_minter(rec_uuid, json_record) index_queue.append(pid.object_uuid) db.session.commit() # Request record indexing for i in index_queue: indexer.index_by_id(i) # Send task to migrate files. return rec_uuid
def test_permission_factory(app, db, action, permission_factory): """Test revisions.""" InvenioAccess(app) rec_uuid = uuid.uuid4() with db.session.begin_nested(): user_all = User(email='*****@*****.**') user_one = User(email='*****@*****.**') user_none = User(email='*****@*****.**') db.session.add(user_all) db.session.add(user_one) db.session.add(user_none) db.session.add(ActionUsers(action=action, user=user_all, argument=None)) db.session.add( ActionUsers(action=action, user=user_one, argument=str(rec_uuid))) record = Record.create({'title': 'permission test'}, id_=rec_uuid) # Create a record and assign permissions. permission = permission_factory(record) # Assert which permissions has access. assert permission.allows(FakeIdentity(UserNeed(user_all.id))) assert permission.allows(FakeIdentity(UserNeed(user_one.id))) assert not permission.allows(FakeIdentity(UserNeed(user_none.id)))
def store_record(obj, *args, **kwargs): """Create and index new record in main record space.""" assert "$schema" in obj.data, "No $schema attribute found!" # Create record # FIXME: Do some preprocessing of obj.data before creating a record so that # we're sure that the schema will be validated without touching the full # holdingpen stack. record = Record.create(obj.data, id_=None) # Create persistent identifier. pid = inspire_recid_minter(str(record.id), record) # Commit any changes to record record.commit() # Dump any changes to record obj.data = record.dumps() # Commit to DB before indexing db.session.commit() # Index record indexer = RecordIndexer() indexer.index_by_id(pid.object_uuid)
def test_reindex(app, script_info): """Test reindex.""" # load records with app.test_request_context(): runner = CliRunner() rec_uuid = uuid.uuid4() data = {'title': 'Test0'} record = Record.create(data, id_=rec_uuid) db.session.commit() # Initialize queue res = runner.invoke(cli.queue, ['init', 'purge'], obj=script_info) assert 0 == res.exit_code res = runner.invoke(cli.reindex, ['--yes-i-know'], obj=script_info) assert 0 == res.exit_code res = runner.invoke(cli.run, [], obj=script_info) assert 0 == res.exit_code sleep(5) indexer = RecordIndexer() index, doc_type = indexer.record_to_index(record) res = current_search_client.get(index=index, doc_type=doc_type, id=rec_uuid) assert res['found'] # Destroy queue res = runner.invoke(cli.queue, ['delete'], obj=script_info) assert 0 == res.exit_code
def create_record(ctx): """ Creates the record in the database. :param ctx: The record metadata as a dictionary. :return: the recid and the uuid """ record_information = create_data_structure(ctx) record_id = uuid.uuid4() pid = recid_minter(record_id, record_information) record_information['recid'] = int(pid.pid_value) record_information['uuid'] = str(record_id) Record.create(record_information, id_=record_id) db.session.commit() return record_information
def news(): """Load demo news records.""" from invenio_db import db from invenio_records import Record from invenio_indexer.api import RecordIndexer from cernopendata.modules.records.minters.artid import \ cernopendata_articleid_minter indexer = RecordIndexer() schema = current_app.extensions['invenio-jsonschemas'].path_to_url( 'records/article-v1.0.0.json' ) data = pkg_resources.resource_filename('cernopendata', 'modules/fixtures/data') articles_json = glob.glob(os.path.join(data, 'articles', 'news', '*.json')) for filename in articles_json: with open(filename, 'rb') as source: for data in json.load(source): if "collections" not in data and \ not isinstance(data.get("collections", None), basestring): data["collections"] = [] data["collections"].append({"primary": "News"}) id = uuid.uuid4() cernopendata_articleid_minter(id, data) record = Record.create(data, id_=id) record['$schema'] = schema db.session.commit() indexer.index(record) db.session.expunge_all()
def records(pids, db): """Fixture for the records.""" pid_versions = ['h1v1', 'h1v2', 'h2v1'] schema = { 'type': 'object', 'properties': { 'title': { 'type': 'string' }, }, } data = { name: { 'title': 'Test version {}'.format(name), 'recid': pids[name].pid_value, '$schema': schema } for name in pid_versions } records = dict() for name in pid_versions: record = Record.create(data[name]) pids[name].assign('rec', record.id) records[name] = record return records
def test_record_replace_refs(app): """Test the replacement of JSON references using JSONResolver.""" with app.app_context(): record = Record.create({ 'one': {'$ref': 'http://nest.ed/A'}, 'three': {'$ref': 'http://nest.ed/ABC'} }) app.extensions['invenio-records'].loader_cls = json_loader_factory( JSONResolver(plugins=['demo.json_resolver'])) out_json = record.replace_refs() expected_json = { 'one': { 'letter': 'A', 'next': '.', }, 'three': { 'letter': 'A', 'next': { 'letter': 'B', 'next': { 'letter': 'C', 'next': '.', }, }, }, } assert out_json == expected_json
def test_valid_put(app): """Test VALID record patch request (PATCH .../records/<record_id>).""" with app.app_context(): InvenioRecordsREST(app) # create the record using the internal API internal_record = Record.create(test_data) with app.test_client() as client: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.put(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps(test_data_patched), headers=headers) assert res.status_code == 200 # check that the returned record matches the given data response_data = json.loads(res.get_data(as_text=True)) assert response_data['data'] == test_data_patched # check that an internal record returned id and that it contains # the same data assert 'id' in response_data.keys() internal_record = Record.get_record(response_data['id']) assert internal_record == response_data['data'] # check that the returned self link returns the same data subtest_self_link(response_data, res.headers, client)
def test_record_update_mutable(app, db): """Test updating mutables in a record.""" recid = uuid.UUID("262d2748-ba41-456f-a844-4d043a419a6f") # Create a new record with two mutables, a list and a dict rec = Record.create({"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}}, id_=recid) # Make sure mutables are there before and after commit assert rec == {"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}} db.session.commit() db.session.expunge_all() rec = Record.get_record(recid) assert rec == {"title": "Title", "list": ["foo"], "dict": {"moo": "boo"}} # Set the mutables under key rec["list"] = ["bar"] rec["dict"] = {"eggs": "bacon"} rec.commit() # Make sure it commits to DB assert rec == {"title": "Title", "list": ["bar"], "dict": {"eggs": "bacon"}} db.session.commit() db.session.expunge_all() rec = Record.get_record(recid) assert rec == {"title": "Title", "list": ["bar"], "dict": {"eggs": "bacon"}} # Update the mutables under key rec["list"].append("spam") rec["dict"]["ham"] = "chicken" rec.commit() # Make sure it commits to DB assert rec == {"title": "Title", "list": ["bar", "spam"], "dict": {"eggs": "bacon", "ham": "chicken"}} db.session.commit() db.session.expunge_all() rec = Record.get_record(recid) assert rec == {"title": "Title", "list": ["bar", "spam"], "dict": {"eggs": "bacon", "ham": "chicken"}}
def load_records(app, filename, schema, tries=5): """Try to index records.""" indexer = RecordIndexer() records = [] with app.app_context(): with mock.patch('invenio_records.api.Record.validate', return_value=None): data_filename = pkg_resources.resource_filename( 'invenio_records', filename) records_data = load(data_filename) with db.session.begin_nested(): for item in records_data: record_id = uuid.uuid4() item_dict = dict(marc21.do(item)) item_dict['$schema'] = schema recid_minter(record_id, item_dict) oaiid_minter(record_id, item_dict) record = Record.create(item_dict, id_=record_id) indexer.index(record) records.append(record.id) db.session.commit() # Wait for indexer to finish for i in range(tries): response = current_search_client.search() if response['hits']['total'] >= len(records): break sleep(5) return records
def test_record_dump(app, db): """Test record dump method.""" with app.app_context(): record = Record.create({"foo": {"bar": "Bazz"}}) record_dump = record.dumps() record_dump["foo"]["bar"] = "Spam" assert record_dump["foo"]["bar"] != record["foo"]["bar"]
def test_preprocessor_mixin_record(app): """Test preprocessor mixin.""" with db.session.begin_nested(): recuuid = uuid.uuid4() record = Record.create({ 'title': 'test', 'aref': {'$ref': '#/title'}}, id_=recuuid) record.model.created = datetime(2015, 10, 1, 11, 11, 11, 1) pid = PersistentIdentifier.create( 'recid', '1', object_type='rec', object_uuid=recuuid, status=PIDStatus.REGISTERED) db.session.commit() data = PreprocessorMixin().preprocess_record(pid, record) for k in keys: assert k in data assert data['metadata']['title'] == 'test' assert data['metadata']['aref'] == {'$ref': '#/title'} assert data['created'] == '2015-10-01T11:11:11.000001+00:00' assert data['revision'] == 1 data = PreprocessorMixin(replace_refs=True).preprocess_record( pid, Record({'title': 'test2', 'aref': {'$ref': '#/title'}})) assert data['created'] is None assert data['updated'] is None assert data['metadata']['aref'] == 'test2'
def test_permission_factory(app, action, permission_factory): """Test revisions.""" InvenioAccess(app) with app.app_context(): rec_uuid = uuid.uuid4() with db.session.begin_nested(): user_all = User(email='*****@*****.**') user_one = User(email='*****@*****.**') user_none = User(email='*****@*****.**') db.session.add(user_all) db.session.add(user_one) db.session.add(user_none) db.session.add(ActionUsers(action=action, user=user_all, argument=None)) db.session.add(ActionUsers(action=action, user=user_one, argument=str(rec_uuid))) record = Record.create({'title': 'permission test'}, id_=rec_uuid) # Create a record and assign permissions. permission = permission_factory(record) # Assert which permissions has access. assert permission.allows(FakeIdentity(UserNeed(user_all.id))) assert permission.allows(FakeIdentity(UserNeed(user_one.id))) assert not permission.allows(FakeIdentity(UserNeed(user_none.id)))
def load_records(app, filename, schema, tries=5): """Try to index records.""" indexer = RecordIndexer() records = [] with app.app_context(): with mock.patch('invenio_records.api.Record.validate', return_value=None): data_filename = pkg_resources.resource_filename( 'invenio_records', filename) records_data = load(data_filename) with db.session.begin_nested(): for item in records_data: record_id = uuid.uuid4() item_dict = dict(marc21.do(item)) item_dict['$schema'] = schema recid_minter(record_id, item_dict) oaiid_minter(record_id, item_dict) record = Record.create(item_dict, id_=record_id) indexer.index(record) records.append(record.id) db.session.commit() # Wait for indexer to finish for i in range(tries): response = current_search_client.search() if response['hits']['total'] >= len(records): break current_search.flush_and_refresh('_all') return records
def test_record_replace_refs(app, db): """Test the replacement of JSON references using JSONResolver.""" record = Record.create({ 'one': { '$ref': 'http://nest.ed/A' }, 'three': { '$ref': 'http://nest.ed/ABC' } }) app.extensions['invenio-records'].loader_cls = json_loader_factory( JSONResolver(plugins=['demo.json_resolver'])) out_json = record.replace_refs() expected_json = { 'one': { 'letter': 'A', 'next': '.', }, 'three': { 'letter': 'A', 'next': { 'letter': 'B', 'next': { 'letter': 'C', 'next': '.', }, }, }, } assert out_json == expected_json
def data_policies(): """Load demo Data Policy records.""" from invenio_db import db from invenio_records import Record from invenio_indexer.api import RecordIndexer from invenio_pidstore.errors import PIDDoesNotExistError, \ PersistentIdentifierError from invenio_pidstore.models import PIDStatus, PersistentIdentifier from invenio_pidstore.fetchers import recid_fetcher from invenio_pidstore.minters import recid_minter from cernopendata.modules.records.minters.recid import \ cernopendata_recid_minter indexer = RecordIndexer() schema = current_app.extensions['invenio-jsonschemas'].path_to_url( 'records/data-policies-v1.0.0.json' ) data = pkg_resources.resource_filename('cernopendata', 'modules/fixtures/data') data_policies_json = glob.glob(os.path.join(data, '*.json')) for filename in data_policies_json: with open(filename, 'rb') as source: for data in json.load(source): id = uuid.uuid4() cernopendata_recid_minter(id, data) record = Record.create(data, id_=id) record['$schema'] = schema db.session.commit() indexer.index(record) db.session.expunge_all()
def create_record(data): """Create a test record.""" with db.session.begin_nested(): data = copy.deepcopy(data) rec_uuid = uuid.uuid4() pid = current_pidstore.minters['cds_recid'](rec_uuid, data) record = Record.create(data, id_=rec_uuid) return pid, record
def create_object(bucket, file_name, stream): """Object creation inside the bucket using the file and its content.""" obj = ObjectVersion.create(bucket, file_name, stream=stream) rec_uuid = uuid4() provider = RecordIdProvider.create(object_type='rec', object_uuid=rec_uuid) data = { 'pid_value': provider.pid.pid_value, 'files': [{ 'uri': '/files/{0}/{1}'.format(str(bucket.id), file_name), 'key': file_name, 'size': obj.file.size, 'bucket': str(bucket.id), 'local': True }] } Record.create(data, id_=rec_uuid)
def create_record(data): """Create a test record.""" with db.session.begin_nested(): data = copy.deepcopy(data) rec_uuid = uuid.uuid4() pid = current_pidstore.minters['recid'](rec_uuid, data) record = Record.create(data, id_=rec_uuid) return pid, record
def test_db(): """Test database backend.""" app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get( 'SQLALCHEMY_DATABASE_URI', 'sqlite:///test.db' ) FlaskCLI(app) InvenioDB(app) InvenioRecords(app) with app.app_context(): create_database(db.engine.url) db.create_all() assert len(db.metadata.tables) == 3 data = {'title': 'Test'} from invenio_records.models import RecordMetadata as RM # Create a record with app.app_context(): assert RM.query.count() == 0 record_uuid = Record.create(data).id db.session.commit() assert RM.query.count() == 1 db.session.commit() # Retrieve created record with app.app_context(): record = Record.get_record(record_uuid) assert record.dumps() == data with pytest.raises(NoResultFound): Record.get_record(uuid.uuid4()) record['field'] = True record = record.patch([ {'op': 'add', 'path': '/hello', 'value': ['world']} ]) assert record['hello'] == ['world'] record.commit() db.session.commit() with app.app_context(): record2 = Record.get_record(record_uuid) assert record2.model.version_id == 2 assert record2['field'] assert record2['hello'] == ['world'] db.session.commit() # Cannot commit record without model (i.e. Record.create_record) with app.app_context(): record3 = Record({'title': 'Not possible'}) with pytest.raises(RecordNotCommitableError): record3.commit() with app.app_context(): db.drop_all() drop_database(db.engine.url)
def test_invalid_patch(app): """Test INVALID record patch request (PATCH .../records/<record_id>).""" with app.app_context(): InvenioRecordsREST(app) with app.test_client() as client: # check that PATCH with non existing id will return 404 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=0), data=json.dumps(test_patch), headers=headers) assert res.status_code == 404 # create the record using the internal API internal_record = Record.create(test_data) # check that PATCH with non accepted format will return 406 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'video/mp4')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps(test_patch), headers=headers) assert res.status_code == 406 # check that PATCH with non-json Content-Type will return 400 headers = [('Content-Type', 'video/mp4'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps(test_patch), headers=headers) assert res.status_code == 415 # check that PATCH with invalid json-patch will return 400 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps([{ 'invalid': 'json-patch' }]), headers=headers) assert res.status_code == 400 # check that PATCH with invalid json will return 400 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data='{sdfsdf', headers=headers) assert res.status_code == 400
def test_api(app, tmpdir): """Test view.""" hello = tmpdir.join('hello.txt') hello.write('Hello world!') bye = tmpdir.join('bye.txt') bye.write('Bye bye!') with app.app_context(): record = Record.create({ 'title': 'Greetings', 'document': hello.strpath }) document = Document(record, '/document') assert document.open().read() == hello.read() # Change document uri document.uri = bye.strpath assert record['document'] == bye.strpath assert document.open().read() == bye.read() # Move bye.txt to done.txt done = tmpdir.join('done.txt') document.move(os.path.abspath(done.strpath)) assert document.uri == done.strpath assert record['document'] == done.strpath assert 'Bye bye!' == document.open().read() # Copy done.txt to copy.txt copy = tmpdir.join('copy.txt') copy_patch = document.copy(copy.strpath) assert document.uri == done.strpath assert copy_patch[0]['value'] == copy.strpath assert done.read() == copy.read() # Set hello.txt to done.txt document.setcontents(hello) assert document.open().read() == hello.read() assert done.read() == hello.read() # Set copy.txt to done.txt via path document.setcontents(copy.strpath) assert document.open().read() == copy.read() assert done.read() == copy.read() # Remove done.txt from metadata document.remove() assert document.uri is None assert os.path.exists(done.strpath) # Remove copy.txt from filesystem assert os.path.exists(copy.strpath) document.uri = copy.strpath document.remove(force=True) assert document.uri is None assert not os.path.exists(copy.strpath)
def create_object(bucket, file_name, stream): """Object creation inside the bucket using the file and its content.""" obj = ObjectVersion.create(bucket, file_name, stream=stream) rec_uuid = uuid4() provider = RecordIdProvider.create(object_type='rec', object_uuid=rec_uuid) data = { 'pid_value': provider.pid.pid_value, 'files': [ { 'uri': '/files/{0}/{1}'.format(str(bucket.id), file_name), 'key': file_name, 'size': obj.file.size, 'bucket': str(bucket.id), 'local': True } ] } Record.create(data, id_=rec_uuid)
def test_invalid_patch(app): """Test INVALID record patch request (PATCH .../records/<record_id>).""" with app.app_context(): InvenioRecordsREST(app) with app.test_client() as client: # check that PATCH with non existing id will return 404 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=0), data=json.dumps(test_patch), headers=headers) assert res.status_code == 404 # create the record using the internal API internal_record = Record.create(test_data) # check that PATCH with non accepted format will return 406 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'video/mp4')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps(test_patch), headers=headers) assert res.status_code == 406 # check that PATCH with non-json Content-Type will return 400 headers = [('Content-Type', 'video/mp4'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps(test_patch), headers=headers) assert res.status_code == 415 # check that PATCH with invalid json-patch will return 400 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data=json.dumps([ {'invalid': 'json-patch'} ]), headers=headers) assert res.status_code == 400 # check that PATCH with invalid json will return 400 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] res = client.patch(url_for(blueprint.name + '.' + RecordResource.view_name, record_id=internal_record.model.id), data='{sdfsdf', headers=headers) assert res.status_code == 400
def test_minimal_record(app, db, minimal_record): """Test minimal record.""" # Create record and pid. record = Record.create(minimal_record) record.model.updated = datetime.utcnow() pid = PersistentIdentifier(pid_type='recid', pid_value='2') assert record.validate() is None expected = { u'date_and_time_of_latest_transaction': ( record.model.updated.strftime("%Y%m%d%H%M%S.0")), u'publication_distribution_imprint': [{ 'date_of_publication_distribution': record['publication_date'] }], u'control_number': '123', u'information_relating_to_copyright_status': { 'copyright_status': 'open' }, u'summary': { 'summary': 'My description' }, u'main_entry_personal_name': { 'personal_name': 'Test' }, u'resource_type': { 'type': 'software' }, u'title_statement': { 'title': 'Test' }, u'leader': { 'base_address_of_data': '00000', 'bibliographic_level': 'monograph_item', 'character_coding_scheme': 'marc-8', 'descriptive_cataloging_form': 'unknown', 'encoding_level': 'unknown', 'indicator_count': 2, 'length_of_the_implementation_defined_portion': 0, 'length_of_the_length_of_field_portion': 4, 'length_of_the_starting_character_position_portion': 5, 'multipart_resource_record_level': 'not_specified_or_not_applicable', 'record_length': '00000', 'record_status': 'new', 'subfield_code_count': 2, 'type_of_control': 'no_specified_type', 'type_of_record': 'computer_file', 'undefined': 0, }, } data = marcxml_v1.schema_class().dump(marcxml_v1.preprocess_record( pid=pid, record=record)).data assert_dict(expected, data) marcxml_v1.serialize(pid=pid, record=record)
def test_undelete_no_get(testapp, db): """Test undelete a record.""" record = Record.create({'title': 'test'}) db.session.commit() record.delete() db.session.commit() record.undelete() record.commit() db.session.commit() assert record == {'title': 'test'}
def test_record_replace_refs(app, db): """Test the replacement of JSON references using JSONResolver.""" record = Record.create({"one": {"$ref": "http://nest.ed/A"}, "three": {"$ref": "http://nest.ed/ABC"}}) app.extensions["invenio-records"].loader_cls = json_loader_factory(JSONResolver(plugins=["demo.json_resolver"])) out_json = record.replace_refs() expected_json = { "one": {"letter": "A", "next": "."}, "three": {"letter": "A", "next": {"letter": "B", "next": {"letter": "C", "next": "."}}}, } assert out_json == expected_json
def test_api(app, tmpdir): """Test view.""" hello = tmpdir.join('hello.txt') hello.write('Hello world!') bye = tmpdir.join('bye.txt') bye.write('Bye bye!') with app.app_context(): record = Record.create({'title': 'Greetings', 'document': hello.strpath}) document = Document(record, '/document') assert document.open().read() == hello.read() # Change document uri document.uri = bye.strpath assert record['document'] == bye.strpath assert document.open().read() == bye.read() # Move bye.txt to done.txt done = tmpdir.join('done.txt') document.move(os.path.abspath(done.strpath)) assert document.uri == done.strpath assert record['document'] == done.strpath assert 'Bye bye!' == document.open().read() # Copy done.txt to copy.txt copy = tmpdir.join('copy.txt') copy_patch = document.copy(copy.strpath) assert document.uri == done.strpath assert copy_patch[0]['value'] == copy.strpath assert done.read() == copy.read() # Set hello.txt to done.txt document.setcontents(hello) assert document.open().read() == hello.read() assert done.read() == hello.read() # Set copy.txt to done.txt via path document.setcontents(copy.strpath) assert document.open().read() == copy.read() assert done.read() == copy.read() # Remove done.txt from metadata document.remove() assert document.uri is None assert os.path.exists(done.strpath) # Remove copy.txt from filesystem assert os.path.exists(copy.strpath) document.uri = copy.strpath document.remove(force=True) assert document.uri is None assert not os.path.exists(copy.strpath)
def test_validate_partial(app, db): """Test partial validation.""" schema = { 'properties': { 'a': { 'type': 'string' }, 'b': { 'type': 'string' }, }, 'required': ['b'] } data = {'a': 'hello', '$schema': schema} with app.app_context(): # Test validation on create() # normal validation should fail because 'b' is required with pytest.raises(ValidationError) as exc_info: Record.create(data) assert "'b' is a required property" == exc_info.value.message # validate with a less restrictive validator record = Record.create(data, validator=PartialDraft4Validator) # set wrong data types should fails in any case data_incorrect = copy.deepcopy(data) data_incorrect['a'] = 1 with pytest.raises(ValidationError) as exc_info: Record.create(data_incorrect, validator=PartialDraft4Validator) assert "1 is not of type 'string'" == exc_info.value.message # Test validation on commit() # validation not passing with normal validator with pytest.raises(ValidationError) as exc_info: record.commit() assert "'b' is a required property" == exc_info.value.message # validation passing with less restrictive validator assert data == record.commit(validator=PartialDraft4Validator) # set wrong data types should fails in any case record['a'] = 1 with pytest.raises(ValidationError) as exc_info: record.commit(validator=PartialDraft4Validator)
def test_undelete_with_get(testapp, db): """Test undelete a record.""" record = Record.create({'title': 'test'}) db.session.commit() record.delete() db.session.commit() record = Record.get_record(record.id, with_deleted=True) record.undelete() record.commit() db.session.commit() assert record == {}
def published_record(app, db, schemas, mappings, prepare_es): # let's create a record record_uuid = uuid.uuid4() data = {'title': 'blah', '$schema': schemas['published']} recid_minter(record_uuid, data) rec = Record.create(data, id_=record_uuid) RecordIndexer().index(rec) current_search_client.indices.refresh() current_search_client.indices.flush() return rec
def create_record(app, item_dict, mint_oaiid=True): """Create test record.""" indexer = RecordIndexer() with app.test_request_context(): record_id = uuid.uuid4() recid_minter(record_id, item_dict) if mint_oaiid: oaiid_minter(record_id, item_dict) record = Record.create(item_dict, id_=record_id) indexer.index(record) return record
def test_record_dump(app, db): """Test record dump method.""" with app.app_context(): record = Record.create({ 'foo': { 'bar': 'Bazz', }, }) record_dump = record.dumps() record_dump['foo']['bar'] = 'Spam' assert record_dump['foo']['bar'] != record['foo']['bar']
def test_validate_partial(app, db): """Test partial validation.""" schema = { 'properties': { 'a': {'type': 'string'}, 'b': {'type': 'string'}, }, 'required': ['b'] } data = { 'a': 'hello', '$schema': schema } with app.app_context(): # Test validation on create() # normal validation should fail because 'b' is required with pytest.raises(ValidationError) as exc_info: Record.create(data) assert "'b' is a required property" == exc_info.value.message # validate with a less restrictive validator record = Record.create(data, validator=PartialDraft4Validator) # set wrong data types should fails in any case data_incorrect = copy.deepcopy(data) data_incorrect['a'] = 1 with pytest.raises(ValidationError) as exc_info: Record.create(data_incorrect, validator=PartialDraft4Validator) assert "1 is not of type 'string'" == exc_info.value.message # Test validation on commit() # validation not passing with normal validator with pytest.raises(ValidationError) as exc_info: record.commit() assert "'b' is a required property" == exc_info.value.message # validation passing with less restrictive validator assert data == record.commit(validator=PartialDraft4Validator) # set wrong data types should fails in any case record['a'] = 1 with pytest.raises(ValidationError) as exc_info: record.commit(validator=PartialDraft4Validator)
def datasets(): """Load demo datasets records.""" from invenio_db import db from invenio_records_files.api import Record from invenio_indexer.api import RecordIndexer from cernopendata.modules.records.minters.recid import \ cernopendata_recid_minter from cernopendata.modules.records.minters.datasetid import \ cernopendata_datasetid_minter from invenio_files_rest.models import \ Bucket, FileInstance, ObjectVersion from invenio_records_files.models import RecordsBuckets indexer = RecordIndexer() schema = current_app.extensions['invenio-jsonschemas'].path_to_url( 'records/datasets-v1.0.0.json') data = pkg_resources.resource_filename('cernopendata', 'modules/fixtures/data/datasets') datasets_json = glob.glob(os.path.join(data, '*.json')) for filename in datasets_json: with open(filename, 'rb') as source: for data in json.load(source): files = data.pop('files', None) id = uuid.uuid4() # (TOFIX) Remove if statement in production # as every dataset record should have a doi if data.get('doi', None): cernopendata_datasetid_minter(id, data) else: cernopendata_recid_minter(id, data) record = Record.create(data, id_=id) record['$schema'] = schema bucket = Bucket.create() RecordsBuckets.create(record=record.model, bucket=bucket) for file in files: assert 'uri' in file assert 'size' in file assert 'checksum' in file f = FileInstance.create() filename = file.get("uri").split('/')[-1:][0] f.set_uri(file.get("uri"), file.get("size"), file.get("checksum")) ObjectVersion.create(bucket, filename, _file_id=f.id) db.session.commit() indexer.index(record) db.session.expunge_all()
def test_get_records(app, db): """Test bulk record fetching.""" # Create test records test_records = [ Record.create({"title": "test1"}), Record.create({"title": "to_be_deleted"}), Record.create({"title": "test3"}), ] db.session.commit() test_ids = [record.id for record in test_records] # Fetch test records assert len(Record.get_records(test_ids)) == 3 test_records[1].delete() # should not show deleted db.session.commit() assert len(Record.get_records(test_ids)) == 2 # should show deleted assert len(Record.get_records(test_ids, with_deleted=True)) == 3
def test_get_records(app, db): """Test bulk record fetching.""" # Create test records test_records = [ Record.create({'title': 'test1'}), Record.create({'title': 'to_be_deleted'}), Record.create({'title': 'test3'}), ] db.session.commit() test_ids = [record.id for record in test_records] # Fetch test records assert len(Record.get_records(test_ids)) == 3 test_records[1].delete() # should not show deleted db.session.commit() assert len(Record.get_records(test_ids)) == 2 # should show deleted assert len(Record.get_records(test_ids, with_deleted=True)) == 3
def test_validate_with_format(app, db): """Test that validation can accept custom format rules.""" with app.app_context(): checker = FormatChecker() checker.checks('foo')(lambda el: el.startswith('foo')) data = { 'bar': 'foo', '$schema': { 'properties': { 'bar': { 'format': 'foo' } } } } # test record creation with valid data assert data == Record.create(data) record = Record.create(data, format_checker=checker) # test direct call to validate with valid data assert record.validate(format_checker=checker) is None # test commit with valid data record.commit(format_checker=checker) record['bar'] = 'bar' # test direct call to validate with invalid data with pytest.raises(ValidationError) as excinfo: record.validate(format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value) # test commit with invalid data with pytest.raises(ValidationError) as excinfo: record.commit(format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value) data['bar'] = 'bar' # test record creation with invalid data with pytest.raises(ValidationError) as excinfo: record = Record.create(data, format_checker=checker) assert "'bar' is not a 'foo'" in str(excinfo.value)
def create_record(collection): """Basic test view.""" collection = Collection.query.filter( Collection.name == collection).first_or_404() schema = urljoin(current_app.config.get('JSONSCHEMAS_HOST'), url_for('records.jsonschema', collection=collection.name)) data, pid, recid = construct_record(collection, json.loads(request.get_data()), current_user.id, schema) try: record = Record.create(data, id_=recid) except ValidationError as error: print("============================") print(error.message) print("============================") db.session.rollback() resp = jsonify(**{'message': error.message}) resp.status_code = 400 return resp # Invenio-Indexer is delegating the document inferring to # Invenio-Search which is analysing the string splitting by `/` and # using `.json` to be sure that it cans understand the mapping. record['$schema'] = 'mappings/{0}.json'.format(collection.name.lower()) indexer = RecordIndexer() indexer.index(record) # Creating permission needs for the record action_edit_record = RecordUpdateActionNeed(str(recid)) action_read_record = RecordReadActionNeed(str(recid)) action_index_record = RecordIndexActionNeed(str(recid)) # Giving index, read, write permissions to user/creator db.session.add(ActionUsers.allow(action_edit_record, user=current_user)) db.session.add(ActionUsers.allow(action_read_record, user=current_user)) db.session.add(ActionUsers.allow(action_index_record, user=current_user)) db.session.commit() resp = jsonify(**{'pid': pid}) resp.status_code = 200 return resp
def sample_record(app, test_db): record = Record.create( { "id": "1", "identifier": [ { "value": "oai:server:id", "type": "originalOAI" } ] } ) db_.session.commit() return record
def draft_record(app, db, schemas, mappings, prepare_es): # let's create a record draft_uuid = uuid.uuid4() data = {'title': 'blah', '$schema': schemas['draft'], 'id': '1'} PersistentIdentifier.create(pid_type='drecid', pid_value='1', status=PIDStatus.REGISTERED, object_type='rec', object_uuid=draft_uuid) rec = Record.create(data, id_=draft_uuid) RecordIndexer().index(rec) current_search_client.indices.refresh() current_search_client.indices.flush() return rec
def assign_record_id(record_information, id=None): """ :param record_information: :return: """ if id: record_information['recid'] = id else: record_id = Record.create(record_information).id PersistentIdentifier.create('recid', record_id, object_type='rec', object_uuid=uuid.uuid4(), status=PIDStatus.REGISTERED) # bit redundant, but the recid is used in many places. record_information['control_number'] = record_information['recid']
def record_upsert(json): """Insert or update a record.""" control_number = json.get('control_number', json.get('recid')) if control_number: control_number = int(control_number) pid_type = InspireRecordIdProvider.schema_to_pid_type(json['$schema']) try: pid = PersistentIdentifier.get(pid_type, control_number) record = Record.get_record(pid.object_uuid) record.update(json) record.commit() except PIDDoesNotExistError: record = Record.create(json, id_=None) # Create persistent identifier. inspire_recid_minter(str(record.id), json) return record
def store_record(obj, *args, **kwargs): """Create and index new record in main record space.""" if '$schema' in obj.data: obj.data['$schema'] = url_for( 'invenio_jsonschemas.get_schema', schema_path="records/{0}".format(obj.data['$schema']) ) # Create record rec_uuid = str(Record.create(obj.data, id_=None).id) # Create persistent identifier. pid = inspire_recid_minter(rec_uuid, obj.data) db.session.commit() # Index record indexer = RecordIndexer() indexer.index_by_id(pid.object_uuid)