From b226f3c6c45b6cb18e1f9299a79e0dd95f4150fd Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 01:33:45 +0300 Subject: [PATCH 01/11] ISAICP-5330: Create a constraint to replace the 'NotNull' constraint. --- .../NotNullUnlessFederatedConstraint.php | 12 +++ ...NullUnlessFederatedConstraintValidator.php | 84 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraint.php create mode 100644 web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraintValidator.php diff --git a/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraint.php b/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraint.php new file mode 100644 index 0000000000..37c9082df9 --- /dev/null +++ b/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraint.php @@ -0,0 +1,12 @@ +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())) { + if ($this->fieldValidator->isDefinedInSchema($parent_entity->getEntityTypeId(), $parent_entity->bundle(), $typed_data->getName()) + && $activity = $this->provenanceHelper->loadProvenanceActivity($parent_entity->id())) { + return; + } + } + } + parent::validate($value, $constraint); + } + +} From 2841b373dadf1ee8dff25229e05f28c49f6ef325 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 01:34:17 +0300 Subject: [PATCH 02/11] ISAICP-5330: Override the NotNull constraint. --- web/modules/custom/joinup_federation/joinup_federation.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/modules/custom/joinup_federation/joinup_federation.module b/web/modules/custom/joinup_federation/joinup_federation.module index 1c3fb1b7f2..b202167ddb 100644 --- a/web/modules/custom/joinup_federation/joinup_federation.module +++ b/web/modules/custom/joinup_federation/joinup_federation.module @@ -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; /** @@ -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; } /** From dd310685a34775e2b077049d40e1b19cf4441faa Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 01:34:58 +0300 Subject: [PATCH 03/11] ISAICP-5330: Ensure that non federated fields are editable in a real case scenario. --- .../features/joinup_federation/import.feature | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/features/joinup_federation/import.feature b/tests/features/joinup_federation/import.feature index b680595071..9064affb00 100644 --- a/tests/features/joinup_federation/import.feature +++ b/tests/features/joinup_federation/import.feature @@ -95,6 +95,7 @@ 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: | uri | title | description | state | modification date | @@ -148,11 +149,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 @@ -174,6 +170,16 @@ 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" + # 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" @@ -221,6 +227,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" From e2cfa2a4ac1d5742a553b34c4e0af3514cd3a131 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 10:33:36 +0300 Subject: [PATCH 04/11] ISAICP-5330: The rdf_entity_provenance module is now part of the pipeline requirements. --- .../tests/src/Kernel/StagingGraphValidReferenceTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php b/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php index e035d15a70..6e99e730f1 100644 --- a/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php +++ b/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php @@ -25,6 +25,7 @@ class StagingGraphValidReferenceTest extends KernelTestBase { 'joinup_sparql', 'rdf_draft', 'rdf_entity', + 'rdf_entity_provenance', 'sparql_entity_storage', 'user', ]; From 5eece0a9bb98cc622faa7620c941cbba31db0495 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 11:00:00 +0300 Subject: [PATCH 05/11] ISAICP-5330: Provide an owner for newly created entities. --- .../src/Plugin/pipeline/Step/Import.php | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/web/modules/custom/joinup_federation/src/Plugin/pipeline/Step/Import.php b/web/modules/custom/joinup_federation/src/Plugin/pipeline/Step/Import.php index 124a850d2d..40dd230152 100644 --- a/web/modules/custom/joinup_federation/src/Plugin/pipeline/Step/Import.php +++ b/web/modules/custom/joinup_federation/src/Plugin/pipeline/Step/Import.php @@ -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. @@ -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(); From 0945c0229a7dc54d5e4d93eaf9587b8dc10e6c03 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 11:00:37 +0300 Subject: [PATCH 06/11] ISAICP-5330: Test that the new user can have access to the solution edit form. --- .../features/joinup_federation/import.feature | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/tests/features/joinup_federation/import.feature b/tests/features/joinup_federation/import.feature index 9064affb00..e212a93913 100644 --- a/tests/features/joinup_federation/import.feature +++ b/tests/features/joinup_federation/import.feature @@ -9,9 +9,9 @@ Feature: As a site moderator I am able to import RDF files. Scenario: Test the pipeline functionality Given collection: - | uri | http://administracionelectronica.gob.es/ctt | - | title | Spain | - | state | validated | + | uri | http://administracionelectronica.gob.es/ctt | + | title | Spain | + | state | validated | And users: | Username | Roles | | LaDonna | moderator | @@ -97,9 +97,17 @@ Feature: As a site moderator I am able to import RDF files. @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 | @@ -113,6 +121,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 | @@ -180,12 +191,18 @@ Feature: As a site moderator I am able to import RDF files. 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" @@ -269,9 +286,9 @@ Feature: As a site moderator I am able to import RDF files. @joinup_collection Scenario: Test that solutions cannot be re-federated in a different collection. And collection: - | uri | http://administracionelectronica.gob.es/ctt | - | title | Spain | - | state | validated | + | uri | http://administracionelectronica.gob.es/ctt | + | title | Spain | + | state | validated | Given I go to "/admin/content/pipeline/spain/execute" When I attach the file "single_solution_valid_adms.rdf" to "File" From ee4f3b86cd1570faf20976c8a4cb853c0f07e421 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 11:49:20 +0300 Subject: [PATCH 07/11] ISAICP-5330: Provide a post update for appointing an owner to the solutions of 'Spain CTT'. --- .../spain_ctt/spain_ctt.post_update.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/web/modules/custom/joinup_communities/spain_ctt/spain_ctt.post_update.php b/web/modules/custom/joinup_communities/spain_ctt/spain_ctt.post_update.php index d2f2ca5574..f565b296f0 100644 --- a/web/modules/custom/joinup_communities/spain_ctt/spain_ctt.post_update.php +++ b/web/modules/custom/joinup_communities/spain_ctt/spain_ctt.post_update.php @@ -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. @@ -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."; + } +} From 946f41cecf90cd074f602d023dfcc82d35dd96e3 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 15:37:49 +0300 Subject: [PATCH 08/11] ISAICP-5330: Provide a correct validation message as the previous one was missleading. --- .../rdf_schema_field_validation/src/SchemaFieldValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php index 8a4342cd21..d281ea8df7 100644 --- a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php +++ b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php @@ -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 { From e35c4de6d6df2eefae083d3c20d9b852f13bba50 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 15:38:27 +0300 Subject: [PATCH 09/11] ISAICP-5330: Provide a check for the schema definition existense. --- .../src/SchemaFieldValidator.php | 8 ++++++++ .../src/SchemaFieldValidatorInterface.php | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php index d281ea8df7..744de0c109 100644 --- a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php +++ b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidator.php @@ -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. * diff --git a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidatorInterface.php b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidatorInterface.php index c2f22b7238..3483b937be 100644 --- a/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidatorInterface.php +++ b/web/modules/custom/rdf_schema_field_validation/src/SchemaFieldValidatorInterface.php @@ -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; + } From 44c292ef337406d645a3e4699907458128116642 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 15:39:08 +0300 Subject: [PATCH 10/11] ISAICP-5330: Check if the bundle can validate its fields prior to checking the definition. --- .../NotNullUnlessFederatedConstraintValidator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraintValidator.php b/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraintValidator.php index 76cbfcd6a9..fbbd81c941 100644 --- a/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraintValidator.php +++ b/web/modules/custom/joinup_federation/src/Plugin/Validation/Constraint/NotNullUnlessFederatedConstraintValidator.php @@ -72,8 +72,11 @@ public function validate($value, Constraint $constraint) { // 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())) { - if ($this->fieldValidator->isDefinedInSchema($parent_entity->getEntityTypeId(), $parent_entity->bundle(), $typed_data->getName()) - && $activity = $this->provenanceHelper->loadProvenanceActivity($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; } } From 2f9b1d30eccd4407d09c673ba9e3b26a08c9e1d2 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos Date: Thu, 27 Jun 2019 15:39:48 +0300 Subject: [PATCH 11/11] ISAICP-5330: The rdf_schema_field_validation is now part of the dependencies as well. --- .../tests/src/Kernel/StagingGraphValidReferenceTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php b/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php index 6e99e730f1..a8b9c97cc3 100644 --- a/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php +++ b/web/modules/custom/joinup_federation/tests/src/Kernel/StagingGraphValidReferenceTest.php @@ -26,6 +26,7 @@ class StagingGraphValidReferenceTest extends KernelTestBase { 'rdf_draft', 'rdf_entity', 'rdf_entity_provenance', + 'rdf_schema_field_validation', 'sparql_entity_storage', 'user', ];