Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Isaicp 5330: Ensure editable non-federated fields. (#1684)
Browse files Browse the repository at this point in the history
Isaicp 5330: Ensure editable non-federated fields.
  • Loading branch information
claudiu-cristea authored Jul 12, 2019
2 parents 960bff0 + 8e6adae commit ecbc428
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 9 deletions.
44 changes: 37 additions & 7 deletions tests/features/joinup_federation/import.feature
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,19 @@ Feature: As a site moderator I am able to import RDF files.
| This value should not be null. |
| The distribution Windows is linked also by the Asset release 1 release. |

@terms
Scenario: Test a successful import.
Given solutions:
Given user:
| Username | CS Owner |
| First name | Collection and solution |
| Family name | Owner |
| E-mail | csowner@example.com |
And solutions:
| uri | title | description | state | modification date |
| http://example.com/solution/2 | Local version of Solution 2 | Initial description | validated | 15-07-2018 |
And the following solution user membership:
| solution | user | roles | state |
| Local version of Solution 2 | CS Owner | facilitator, administrator | active |
And the following distribution:
| uri | http://example.com/distribution/3 |
| title | Local version of a standalone distribution |
Expand All @@ -120,6 +129,9 @@ Feature: As a site moderator I am able to import RDF files.
| title | Spain |
| state | validated |
| affiliates | Local version of Solution 2 |
And the following collection user membership:
| collection | user | roles | state |
| Spain | CS Owner | facilitator, administrator | active |
And provenance activities:
| entity | enabled | associated with | author | started |
| Local version of Solution 2 | yes | Spain | Antoine Batiste | 2012-07-07 23:01 |
Expand Down Expand Up @@ -156,11 +168,6 @@ Feature: As a site moderator I am able to import RDF files.
| The Spain - Center for Technology Transfer execution has finished with success. |
And I should see the heading "Successfully executed Spain - Center for Technology Transfer import pipeline"

# Check that the existing solution values were overridden.
Given I go to the "Solution 2" solution edit form
Then the "Title" field should contain "Solution 2"
And the "Description" field should contain "This solution has a standalone distribution."

# Check how the provenance records were created/updated.
Then the "Solution 1" entity is not blacklisted for federation
And the "Solution 2" entity is not blacklisted for federation
Expand All @@ -182,12 +189,28 @@ Feature: As a site moderator I am able to import RDF files.
But the "Solution 1" solution should be affiliated with the "Spain" collection
And the "Solution 2" solution should be affiliated with the "Spain" collection

# Check that the existing solution values were overridden.
Given I go to the "Solution 2" solution edit form
Then the "Title" field should contain "Solution 2"
And the "Description" field should contain "This solution has a standalone distribution."

# Verify the user can edit non federated values.
And I select "E-health" from "Policy domain"
And I press "Publish"
Then I should see the heading "Solution 2"

# Verify that the collection owner can edit the new solutions.
When I am logged in as "CS Owner"
And I go to the "Solution 1" solution edit form
Then the response status code should be 200

# Ensure that the og relation is set between the distribution and the solution.
When I go to the "Windows" asset distribution
Then I should see the heading "Solution 1"

# Re-import.
Given I visit "/admin/content/pipeline/spain/execute"
Given I am logged in as "Antoine Batiste"
And I visit "/admin/content/pipeline/spain/execute"
And I attach the file "valid_adms.rdf" to "File"
And I press "Upload"

Expand Down Expand Up @@ -229,6 +252,13 @@ Feature: As a site moderator I am able to import RDF files.
And the "Solution 2" solution should be affiliated with the "Spain" collection
And the "Solution 3" solution should be affiliated with the "Spain" collection

# Check that the Policy domain value was not overridden.
Given I go to the "Solution 2" solution edit form
# The text is '-E-health' because of the '-' prepended to children options.
And the option with text "-E-health" from select "Policy domain" is selected
And I press "Publish"
Then I should see the heading "Solution 2"

# Check that provenance activity records are not indexed.
When I am at "/search"
Then I should not see the following facet items "Activities"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
* Post update functions for the Spain CTT module.
*/

use Drupal\og\Entity\OgRole;
use Drupal\og\OgMembershipInterface;
use Drupal\rdf_entity\Entity\Rdf;
use Drupal\rdf_entity\RdfInterface;
use Drupal\redirect\Entity\Redirect;
use Drupal\user\Entity\User;

/**
* Clean more solution duplicates in the ctt collection.
Expand Down Expand Up @@ -171,3 +175,46 @@ function spain_ctt_post_update_delete_ctt_entities() {
$rdf_entity_storage->delete($rdf_entities_to_delete);
$node_storage->delete($nodes_to_delete);
}

/**
* Sets the owner of the "Spain CTT" collection to all its solutions.
*/
function spain_ctt_post_update_set_solutions_owner(array &$sandbox) {
if (!isset($sandbox['solution_ids'])) {
$id = 'http://administracionelectronica.gob.es/ctt';
$spain_ctt = Rdf::load($id);
$solutions = $spain_ctt->field_ar_affiliates->referencedEntities();

$sandbox['solution_ids'] = array_map(function (RdfInterface $solution): string {
return $solution->id();
}, $solutions);
$sandbox['owner_id'] = $spain_ctt->getOwnerId();
$sandbox['processed'] = 0;
}

$solution_ids = array_splice($sandbox['solution_ids'], 0, 10);
$user = User::load($sandbox['owner_id']);
$roles = [
OgRole::getRole('rdf_entity', 'solution', 'administrator'),
OgRole::getRole('rdf_entity', 'solution', 'facilitator'),
];

/** @var \Drupal\og\MembershipManagerInterface $membership_manager */
$membership_manager = \Drupal::service('og.membership_manager');
foreach (Rdf::loadMultiple($solution_ids) as $solution) {
if (!empty($membership_manager->getMembership($solution, $user, OgMembershipInterface::ALL_STATES))) {
continue;
}
$membership = $membership_manager->createMembership($solution, $user);
$membership->setRoles($roles);
$membership->save();

$sandbox['processed']++;
}

$sandbox['#finished'] = (int) !$sandbox['solution_ids'];

if ($sandbox['#finished'] === 1) {
return "{$sandbox['processed']} memberships were appointed.";
}
}
2 changes: 2 additions & 0 deletions web/modules/custom/joinup_federation/joinup_federation.module
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use Drupal\joinup_federation\Plugin\Validation\Constraint\FederationDistribution
use Drupal\joinup_federation\Plugin\Validation\Constraint\JoinupEntityChangedConstraint;
use Drupal\joinup_federation\Plugin\Validation\Constraint\JoinupReferenceAccessConstraint;
use Drupal\joinup_federation\Plugin\Validation\Constraint\JoinupValidReferenceConstraint;
use Drupal\joinup_federation\Plugin\Validation\Constraint\NotNullUnlessFederatedConstraint;
use Drupal\joinup_federation\RdfEntityReferenceFieldItemList;

/**
Expand Down Expand Up @@ -67,6 +68,7 @@ function joinup_federation_validation_constraint_alter(array &$definitions) {
$definitions['ReferenceAccess']['class'] = JoinupReferenceAccessConstraint::class;
$definitions['EntityChanged']['class'] = JoinupEntityChangedConstraint::class;
$definitions['DistributionSingleParent']['class'] = FederationDistributionSingleParentConstraint::class;
$definitions['NotNull']['class'] = NotNullUnlessFederatedConstraint::class;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Drupal\joinup_federation\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraints\NotNull;

/**
* NotNull constraint.
*
* Overrides the Drupal Core validation to handle federated entities.
*/
class NotNullUnlessFederatedConstraint extends NotNull {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Drupal\joinup_federation\Plugin\Validation\Constraint;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraintValidator;
use Drupal\rdf_entity\RdfInterface;
use Drupal\rdf_entity_provenance\ProvenanceHelperInterface;
use Drupal\rdf_schema_field_validation\SchemaFieldValidatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;

/**
* NotNull constraint validator.
*
* Overrides the Drupal NotNull validator to allow empty when it refers to a
* field that belongs to a federated entity.
*
* Used for fields that have taxonomy references that are not provided into, or
* is not mapped in the federated record.
*/
class NotNullUnlessFederatedConstraintValidator extends NotNullConstraintValidator implements ContainerInjectionInterface {

use TypedDataAwareValidatorTrait;

/**
* The provenance helper service.
*
* @var \Drupal\rdf_entity_provenance\ProvenanceHelperInterface
*/
protected $provenanceHelper;

/**
* The field validator service.
*
* @var \Drupal\rdf_schema_field_validation\SchemaFieldValidatorInterface
*/
protected $fieldValidator;

/**
* Creates a new validator.
*
* @param \Drupal\rdf_entity_provenance\ProvenanceHelperInterface $provenance_helper
* The provenance helper service.
* @param \Drupal\rdf_schema_field_validation\SchemaFieldValidatorInterface $field_validator
* The field validator service.
*/
public function __construct(ProvenanceHelperInterface $provenance_helper, SchemaFieldValidatorInterface $field_validator) {
$this->provenanceHelper = $provenance_helper;
$this->fieldValidator = $field_validator;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('rdf_entity_provenance.provenance_helper'),
$container->get('rdf_schema_field_validation.schema_field_validator')
);
}

/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
$typed_data = $this->getTypedData();
if ($typed_data instanceof ListInterface) {
$parent_entity = $value->getParent()->getEntity();
// Check for the parent entity being an rdf entity so that we quickly skip
// other entity types that use the NotNull constraint.
if (($parent_entity instanceof RdfInterface) && !empty($parent_entity->id())) {
$entity_type_id = $parent_entity->getEntityTypeId();
$bundle = $parent_entity->bundle();
if ($this->fieldValidator->hasSchemaDefinition($entity_type_id, $bundle)
&& $this->fieldValidator->isDefinedInSchema($entity_type_id, $bundle, $typed_data->getName())
&& $this->provenanceHelper->loadProvenanceActivity($parent_entity->id())) {
return;
}
}
}
parent::validate($value, $constraint);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,26 @@ public static function create(ContainerInterface $container, array $configuratio
* {@inheritdoc}
*/
public function initBatchProcess() {
$owner_id = NULL;
$membership_storage = $this->entityTypeManager->getStorage('og_membership');
$memberships = $membership_storage->loadByProperties([
'entity_type' => 'rdf_entity',
'entity_bundle' => 'collection',
'entity_id' => $this->pipeline->getCollection(),
'roles' => 'rdf_entity-collection-administrator',
]);

// Normally, there is always an owner for every collection. However, since
// there is no constraint for whether a collection is created without an
// owner (e.g. directly through the API), and there are cases where it is
// not (e.g. tests), we silently avoid an error in the pipeline which will
// occur during the import phase.
if ($membership = reset($memberships)) {
$owner_id = $membership->getOwnerId();
}

$this->setBatchValue('owner_id', $owner_id);

// Retrieve the list of entities from the persistent data store as an
// associative array keyed by entity ID and having a boolean as value,
// signaling if the entity already exists in Joinup.
Expand Down Expand Up @@ -156,7 +176,8 @@ public function execute() {
else {
$local_entity = (clone $entity)
->enforceIsNew()
->set('graph', SparqlGraphInterface::DEFAULT);
->set('graph', SparqlGraphInterface::DEFAULT)
->set('uid', $this->getBatchValue('owner_id'));
// Delete the incoming entity from the staging graph.
$entity->skip_notification = TRUE;
$entity->delete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class StagingGraphValidReferenceTest extends KernelTestBase {
'joinup_sparql',
'rdf_draft',
'rdf_entity',
'rdf_entity_provenance',
'rdf_schema_field_validation',
'sparql_entity_storage',
'user',
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function __construct(ConnectionInterface $sparql_endpoint, EntityTypeMana
public function isDefinedInSchema(string $entity_type_id, string $bundle, string $field_name, string $column_name = ''): bool {
$mapping = $this->getEntityMapping($entity_type_id, $bundle);
if (empty($mapping) || empty($properties = $mapping->getThirdPartySettings('rdf_schema_field_validation'))) {
throw new \Exception("The entity does not appear to have mapped properties.");
throw new \Exception('The entity bundle does not appear to have a schema associated with it so it cannot validate the mapping of its fields.');
}

try {
Expand All @@ -81,6 +81,14 @@ public function isDefinedInSchema(string $entity_type_id, string $bundle, string
return $this->sparqlEndpoint->query($query)->isTrue();
}

/**
* {@inheritdoc}
*/
public function hasSchemaDefinition(string $entity_type_id, string $bundle): bool {
$mapping = $this->getEntityMapping($entity_type_id, $bundle);
return !empty($mapping) && !empty($mapping->getThirdPartySettings('rdf_schema_field_validation'));
}

/**
* Retrieves an SparqlMapping entity.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,21 @@ interface SchemaFieldValidatorInterface {
*/
public function isDefinedInSchema(string $entity_type_id, string $bundle, string $field_name, string $column = ''): bool;

/**
* Checks whether an entity bundle has a validation schema definition.
*
* This is different with the field mapping itself as an entity can have a
* mapping but it does not necessarily have to be part of an ontology.
*
* @param string $entity_type_id
* The entity type id.
* @param string $bundle
* The bundle id.
*
* @return bool
* Whether the bundle has a schema associated with it and can validate
* field schema definitions.
*/
public function hasSchemaDefinition(string $entity_type_id, string $bundle): bool;

}

0 comments on commit ecbc428

Please sign in to comment.