def test_valid_get_community_schema(app, test_communities): """Test VALID community get request (GET .../communities/<id>).""" with app.app_context(): community_id = str(test_communities['MyTestCommunity1']) with app.test_client() as client: for version in [0, 1, 'last']: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.get( url_for('b2share_schemas.community_schema_item', community_id=community_id, schema_version_nb=version), headers=headers) assert res.status_code == 200 # check that the returned community matches the given data response_data = json.loads(res.get_data(as_text=True)) if isinstance(version, int): expected = CommunitySchema.get_community_schema( community_id, version) elif version == 'last': expected = CommunitySchema.get_community_schema( community_id) else: raise NotImplementedError('Test not implemented') assert (expected.build_json_schema() == response_data['json_schema']) assert response_data['community'] == community_id assert response_data['version'] == expected.version with app.app_context(): with app.test_client() as client: # check that the returned self link returns the same data subtest_self_link(response_data, res.headers, client)
def test_valid_get_community_schema(app, test_communities): """Test VALID community get request (GET .../communities/<id>).""" with app.app_context(): community_id = str(test_communities['MyTestCommunity1']) with app.test_client() as client: for version in [0, 1, 'last']: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.get(url_for( 'b2share_schemas.community_schema_item', community_id=community_id, schema_version_nb=version), headers=headers) assert res.status_code == 200 # check that the returned community matches the given data response_data = json.loads(res.get_data(as_text=True)) if isinstance(version, int): expected = CommunitySchema.get_community_schema( community_id, version) elif version == 'last': expected = CommunitySchema.get_community_schema( community_id) else: raise NotImplementedError('Test not implemented') assert (expected.build_json_schema() == response_data['json_schema']) assert response_data['community'] == community_id assert response_data['version'] == expected.version with app.app_context(): with app.test_client() as client: # check that the returned self link returns the same data subtest_self_link(response_data, res.headers, client)
def validate(self, **kwargs): if ('publication_state' in self and self['publication_state'] == PublicationStates.draft.name): if 'community' not in self: raise ValidationError('Missing required field "community"') try: community_id = uuid.UUID(self['community']) except (ValueError, KeyError) as e: raise InvalidDepositError('Community ID is not a valid UUID.') \ from e default_validator = validator_for( CommunitySchema.get_community_schema( community_id).build_json_schema()) if 'required' not in default_validator.VALIDATORS: raise NotImplementedError('B2Share does not support schemas ' 'which have no "required" keyword.') DraftDepositValidator = type( 'DraftDepositValidator', (default_validator, ), dict(VALIDATORS=copy.deepcopy(default_validator.VALIDATORS))) # function ignoring the validation of the given keyword ignore = lambda *args, **kwargs: None DraftDepositValidator.VALIDATORS['required'] = ignore DraftDepositValidator.VALIDATORS['minItems'] = ignore kwargs['validator'] = DraftDepositValidator return super(Deposit, self).validate(**kwargs)
def community_to_dict(community): ret = dict(id=community.id, name=community.name, description=community.description, logo=community.logo, created=community.created, updated=community.updated, publication_workflow=community.publication_workflow, restricted_submission=community.restricted_submission, links=dict(self=community_self_link(community, _external=True), ), roles=dict( admin=dict(id=community.admin_role.id, name=community.admin_role.name, description=community.admin_role.description), member=dict(id=community.member_role.id, name=community.member_role.name, description=community.member_role.description), )) try: community_schema = CommunitySchema.get_community_schema(community.id) community_schema_dict = json.loads(community_schema.community_schema) ret['links']['schema'] = community_schema_json_schema_link( community_schema, _external=True) ret['links']['schemas'] = community_schema_json_schemas_link( community_schema, _external=True) ret['schema'] = dict(version=community_schema.version, root_schema=community_schema.root_schema) props = community_schema_dict.get('properties', {}) if props: ret['links']['block_schema'] = next(iter(props.values()))['$ref'] ret['schema']['block_schema_id'] = next(iter(props)) finally: return ret
def validate(self, **kwargs): if ('publication_state' in self and self['publication_state'] == PublicationStates.draft.name): if 'community' not in self: raise ValidationError('Missing required field "community"') try: community_id = uuid.UUID(self['community']) except (ValueError, KeyError) as e: raise InvalidDepositError('Community ID is not a valid UUID.') \ from e default_validator = validator_for( CommunitySchema.get_community_schema(community_id).build_json_schema()) if 'required' not in default_validator.VALIDATORS: raise NotImplementedError('B2Share does not support schemas ' 'which have no "required" keyword.') DraftDepositValidator = type( 'DraftDepositValidator', (default_validator,), dict(VALIDATORS=copy.deepcopy(default_validator.VALIDATORS)) ) # function ignoring the validation of the given keyword ignore = lambda *args, **kwargs: None DraftDepositValidator.VALIDATORS['required'] = ignore DraftDepositValidator.VALIDATORS['minItems'] = ignore kwargs['validator'] = DraftDepositValidator return super(Deposit, self).validate(**kwargs)
def test_new_deposit_versions_preserve_schema(app, test_records, test_users): """Creating new versions of existing records.""" with app.app_context(): # Retrieve a record which will be the first version, and its deposit v1_rec = B2ShareRecord.get_record(test_records[0].record_id) _, v1_id = pid_of(test_records[0].data) # update the community's schema community_id = v1_rec.model.json['community'] old_schema = CommunitySchema.get_community_schema(community_id) json_schema = json.loads(old_schema.model.community_schema) new_schema = CommunitySchema.create_version(community_id, json_schema) assert new_schema.version > old_schema.version # create new, unversioned, draft and records data = copy_data_from_previous(v1_rec.model.json) unver_dep = create_deposit(data, test_users['deposits_creator']) unver_dep.submit() unver_dep.publish() _, unver_rec = unver_dep.fetch_published() # the unversioned draft or record in a version chain have the updated schema assert unver_dep['$schema'] != Deposit._build_deposit_schema( v1_rec.model.json) assert unver_rec.model.json['$schema'] != v1_rec.model.json['$schema'] # create new draft and record in the version chain of v1 data = copy_data_from_previous(v1_rec.model.json) v2_dep = create_deposit(data, test_users['deposits_creator'], version_of=v1_id) v2_dep.submit() v2_dep.publish() _, v2_rec = v2_dep.fetch_published() # the new draft and record in a version chain preserve the version of the schema assert v2_dep['$schema'] == Deposit._build_deposit_schema( v1_rec.model.json) assert v2_rec.model.json['$schema'] == v1_rec.model.json['$schema']
def create(cls, data, id_=None): """Create a deposit.""" # check that the status field is not set if 'publication_state' in data: raise InvalidDepositError( 'Field "publication_state" cannot be set.') data['publication_state'] = PublicationStates.draft.name # Set record's schema if '$schema' in data: raise InvalidDepositError('"$schema" field should not be set.') if 'community' not in data or not data['community']: raise ValidationError( 'Record\s metadata has no community field.') try: community_id = uuid.UUID(data['community']) except ValueError as e: raise InvalidDepositError( 'Community ID is not a valid UUID.') from e try: schema = CommunitySchema.get_community_schema(community_id) except CommunitySchemaDoesNotExistError as e: raise InvalidDepositError( 'No schema for community {}.'.format(community_id)) from e from b2share.modules.schemas.serializers import \ community_schema_draft_json_schema_link data['$schema'] = community_schema_draft_json_schema_link( schema, _external=True ) deposit = super(Deposit, cls).create(data, id_=id_) # create file bucket bucket = deposit._create_bucket() db.session.add(bucket) db.session.add(RecordsBuckets( record_id=deposit.id, bucket_id=bucket.id )) return deposit
def create(cls, data, id_=None, version_of=None): """Create a deposit with the optional id. :params version_of: PID of an existing record. If set, the new record will be marked as a new version of this referenced record. If no data is provided the new record will be a copy of this record. Note: this PID must reference the current last version of a record. """ # check that the status field is not set if 'publication_state' in data: raise InvalidDepositError( 'Field "publication_state" cannot be set.') data['publication_state'] = PublicationStates.draft.name # Set record's schema if '$schema' in data: raise InvalidDepositError('"$schema" field should not be set.') # Retrieve reserved record PID which should have already been created # by the deposit minter (The record PID value is the same # as the one of the deposit) rec_pid = RecordUUIDProvider.get(data['_deposit']['id']).pid version_master, prev_version = None, None # if this is a new version of an existing record, add the future # record pid in the chain of versions. if version_of: version_master, prev_version = \ find_version_master_and_previous_record(version_of) # The new version must be in the same community if data['community'] != prev_version['community']: raise ValidationError( 'The community field cannot change between versions.') try: version_master.insert_draft_child(rec_pid) except Exception as exc: # Only one draft is allowed per version chain. if 'Draft child already exists for this relation' in \ exc.args[0]: raise DraftExistsVersioningError( version_master.draft_child ) raise exc else: # create parent PID parent_pid = RecordUUIDProvider.create().pid version_master = PIDNodeVersioning(parent=parent_pid) version_master.insert_draft_child(child=rec_pid) # Mint the deposit with the parent PID data['_pid'] = [{ 'value': version_master.parent.pid_value, 'type': RecordUUIDProvider.parent_pid_type, }] if 'community' not in data or not data['community']: raise ValidationError( 'Record\s metadata has no community field.') try: community_id = uuid.UUID(data['community']) except ValueError as e: raise InvalidDepositError( 'Community ID is not a valid UUID.') from e try: schema = CommunitySchema.get_community_schema(community_id) except CommunitySchemaDoesNotExistError as e: raise InvalidDepositError( 'No schema for community {}.'.format(community_id)) from e if version_of: data['$schema'] = Deposit._build_deposit_schema(prev_version) else: from b2share.modules.schemas.serializers import \ community_schema_draft_json_schema_link data['$schema'] = community_schema_draft_json_schema_link( schema, _external=True ) # create file bucket if prev_version and prev_version.files: # Clone the bucket from the previous version. This doesn't # duplicate files. bucket = prev_version.files.bucket.snapshot(lock=False) bucket.locked = False else: bucket = Bucket.create(storage_class=current_app.config[ 'DEPOSIT_DEFAULT_STORAGE_CLASS' ]) if 'external_pids' in data: create_b2safe_file(data['external_pids'], bucket) del data['external_pids'] deposit = super(Deposit, cls).create(data, id_=id_) db.session.add(bucket) db.session.add(RecordsBuckets( record_id=deposit.id, bucket_id=bucket.id )) return deposit
def test_new_community_set_schema_cmd(app, login_user, tmp_location): """Test adding a community and setting its schema using CLI commands.""" with app.app_context(): tmp_location.default = True db.session.merge(tmp_location) db.session.commit() runner = CliRunner() script_info = ScriptInfo(create_app=lambda info: app) comm_name = 'MyCom' # Create a new community on the command line args = ['create', comm_name, 'MyCom description', 'eudat.png'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community = Community.get(name=comm_name) assert community assert community.name == comm_name # Create a schema for this new community with runner.isolated_filesystem(): with open("schema.json", "w") as f: f.write(json.dumps(test_schema)) # check RootSchemaDoesNotExistError with pytest.raises(RootSchemaDoesNotExistError): update_or_set_community_schema(comm_name, 'schema.json') # check RootSchemaDoesNotExistError via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code != 0 # initialize the root schema in the test environment result = runner.invoke(schemas_cmd, ['init', '--ignore-mismatches'], obj=script_info) assert result.exit_code == 0 # Make custom schema via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community_name = 'MyCom' community = Community.get(name=community_name) # Get the block schema id community_schema = CommunitySchema.get_community_schema( community.id).community_schema props = json.loads(community_schema).get('properties') assert len(props) == 1 block_schema_id = props.popitem()[0] # Test the community schema by publishing a record with app.test_client() as client: user = create_user('allowed') login_user(user, client) record_data = { 'titles': [{ 'title': 'My Test Record' }], 'community': str(community.id), 'open_access': True, 'community_specific': { block_schema_id: { 'test_field1': "string value", 'test_field2': 10, } }, } headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] draft_res = client.post(url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) draft_data = json.loads(draft_res.get_data(as_text=True)) assert draft_res.status_code == 201 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_submit_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=draft_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": "submitted", }]), headers=headers) assert draft_submit_res.status_code == 200
def test_new_community_set_schema_cmd(app, login_user, tmp_location): """Test adding a community and setting its schema using CLI commands.""" with app.app_context(): tmp_location.default = True db.session.merge(tmp_location) db.session.commit() runner = CliRunner() script_info = ScriptInfo(create_app=lambda info: app) comm_name = 'MyCom' # Create a new community on the command line args = ['create', comm_name, 'MyCom description', 'eudat.png'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community = Community.get(name=comm_name) assert community assert community.name == comm_name # Create a schema for this new community with runner.isolated_filesystem(): with open("schema.json", "w") as f: f.write(json.dumps(test_schema)) # check RootSchemaDoesNotExistError with pytest.raises(RootSchemaDoesNotExistError): update_or_set_community_schema(comm_name, 'schema.json') # check RootSchemaDoesNotExistError via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code != 0 # initialize the root schema in the test environment result = runner.invoke(schemas_cmd, ['init'], obj=script_info) assert result.exit_code == 0 # Make custom schema via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community_name = 'MyCom' community = Community.get(name=community_name) # Get the block schema id community_schema = CommunitySchema.get_community_schema( community.id).community_schema props = json.loads(community_schema).get('properties') assert len(props) == 1 block_schema_id = props.popitem()[0] # Test the community schema by publishing a record with app.test_client() as client: user = create_user('allowed') login_user(user, client) record_data = { 'titles': [{'title':'My Test Record'}], 'community': str(community.id), 'open_access': True, 'community_specific': { block_schema_id: { 'test_field1': "string value", 'test_field2': 10, } }, } headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] draft_res = client.post(url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) draft_data = json.loads(draft_res.get_data(as_text=True)) assert draft_res.status_code == 201 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_submit_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": "submitted", }]), headers=headers) assert draft_submit_res.status_code == 200
def create(cls, data, id_=None, version_of=None): """Create a deposit with the optional id. :params version_of: PID of an existing record. If set, the new record will be marked as a new version of this referenced record. If no data is provided the new record will be a copy of this record. Note: this PID must reference the current last version of a record. """ # check that the status field is not set if 'publication_state' in data: raise InvalidDepositError( 'Field "publication_state" cannot be set.') data['publication_state'] = PublicationStates.draft.name # Set record's schema if '$schema' in data: raise InvalidDepositError('"$schema" field should not be set.') # Retrieve reserved record PID which should have already been created # by the deposit minter (The record PID value is the same # as the one of the deposit) rec_pid = RecordUUIDProvider.get(data['_deposit']['id']).pid version_master, prev_version = None, None # if this is a new version of an existing record, add the future # record pid in the chain of versions. if version_of: version_master, prev_version = \ find_version_master_and_previous_record(version_of) # The new version must be in the same community if data['community'] != prev_version['community']: raise ValidationError( 'The community field cannot change between versions.') try: version_master.insert_draft_child(rec_pid) except Exception as exc: # Only one draft is allowed per version chain. if 'Draft child already exists for this relation' in \ exc.args[0]: raise DraftExistsVersioningError( version_master.draft_child ) raise exc else: # create parent PID parent_pid = RecordUUIDProvider.create().pid version_master = PIDVersioning(parent=parent_pid) version_master.insert_draft_child(child=rec_pid) # Mint the deposit with the parent PID data['_pid'] = [{ 'value': version_master.parent.pid_value, 'type': RecordUUIDProvider.parent_pid_type, }] if 'community' not in data or not data['community']: raise ValidationError( 'Record\s metadata has no community field.') try: community_id = uuid.UUID(data['community']) except ValueError as e: raise InvalidDepositError( 'Community ID is not a valid UUID.') from e try: schema = CommunitySchema.get_community_schema(community_id) except CommunitySchemaDoesNotExistError as e: raise InvalidDepositError( 'No schema for community {}.'.format(community_id)) from e if version_of: data['$schema'] = Deposit._build_deposit_schema(prev_version) else: from b2share.modules.schemas.serializers import \ community_schema_draft_json_schema_link data['$schema'] = community_schema_draft_json_schema_link( schema, _external=True ) # create file bucket if prev_version and prev_version.files: # Clone the bucket from the previous version. This doesn't # duplicate files. bucket = prev_version.files.bucket.snapshot(lock=False) bucket.locked = False else: bucket = Bucket.create(storage_class=current_app.config[ 'DEPOSIT_DEFAULT_STORAGE_CLASS' ]) if 'external_pids' in data: create_b2safe_file(data['external_pids'], bucket) del data['external_pids'] deposit = super(Deposit, cls).create(data, id_=id_) db.session.add(bucket) db.session.add(RecordsBuckets( record_id=deposit.id, bucket_id=bucket.id )) return deposit