With the merging of #2710947: Support replication filters there is now a ReplicationTask that can be used on ReplicatorInterface. This task class allows passing replication settings similar to the CouchDB spec: http://docs.couchdb.org/en/1.6.1/json-structure.html#replication-settings

This task if for utlizing the ReplicationTask on all ReplicatorInterfaces in Workspace.

CommentFileSizeAuthor
#118 interdiff_113-118.txt4.67 KBjosephdpurcell
#118 2749167_118.patch29.78 KBjosephdpurcell
#113 interdiff_107-113.txt1.92 KBjosephdpurcell
#113 interdiff_100-113.txt1.83 KBjosephdpurcell
#113 2749167_113.patch28.08 KBjosephdpurcell
#107 interdiff.txt1.25 KBjeqq
#107 support_replicationtask-2749167-107.patch27.49 KBjeqq
#100 interdiff_96-100.txt747 bytesjosephdpurcell
#100 2749167_100.patch28.18 KBjosephdpurcell
#96 interdiff_90-96.txt877 bytesjosephdpurcell
#96 2749167_96.patch28 KBjosephdpurcell
#90 interdiff_85-90.txt907 bytesjosephdpurcell
#90 2749167_90.patch27.06 KBjosephdpurcell
#85 2749167_85.patch27.94 KBjosephdpurcell
#85 interdiff_80-85.txt1.29 KBjosephdpurcell
#80 interdiff_75-80.txt5.52 KBjosephdpurcell
#80 2749167_80.patch27.53 KBjosephdpurcell
#75 interdiff_65-75.txt2.12 KBjosephdpurcell
#75 2749167_75.patch23.31 KBjosephdpurcell
#65 interdiff_64-65.txt1.55 KBjosephdpurcell
#65 interdiff_58-65.txt5.05 KBjosephdpurcell
#65 2749167_65.patch23.15 KBjosephdpurcell
#64 interdiff_58-64.txt4.07 KBjosephdpurcell
#64 2749167_64.patch22.17 KBjosephdpurcell
#58 interdiff_42-58.txt1.42 KBjosephdpurcell
#58 2749167_58.patch20.01 KBjosephdpurcell
#48 workspace-n2749167-48.patch18.69 KBDamienMcKenna
#48 workspace-n2749167-48.interdiff.txt426 bytesDamienMcKenna
#42 workspace-n2749167-42.interdiff.txt2.49 KBDamienMcKenna
#42 workspace-n2749167-42.patch18.67 KBDamienMcKenna
#40 2749167_40.patch17.98 KBjosephdpurcell
#34 interdiff_29-34.txt1.85 KBjosephdpurcell
#34 2749167_34.patch1.85 KBjosephdpurcell
#29 2749167_29.patch17.89 KBjosephdpurcell
#29 interdiff_24-29.txt9.94 KBjosephdpurcell
#24 interdiff.txt8.7 KBjeqq
#24 support_replicationtask-2749167-24.patch14.42 KBjeqq
#14 2749167_14.patch15.56 KBjosephdpurcell
#13 2749167-test_13.patch3 KBjosephdpurcell
#3 2749167_3.patch13.93 KBjosephdpurcell
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

josephdpurcell created an issue. See original summary.

josephdpurcell’s picture

Issue summary: View changes
josephdpurcell’s picture

This patch is a draft for implementing the ReplicationTask. There are a few @todos to follow up on during review:

  1. +++ b/src/EventSubscriber/WorkbenchModerationSubscriber.php
    @@ -79,6 +79,7 @@ class WorkbenchModerationSubscriber implements EventSubscriberInterface {
    +    // @todo pass a ReplicationTask to replicate()
    

    Should we just create an empty ReplicationTask to pass here? If so, probably create it in "onTransition" method and pass here to "mergeWorkspaceToParent" as a parameter.

  2. +++ b/src/Plugin/RulesAction/ReplicateContent.php
    @@ -99,6 +99,7 @@ class ReplicateContent extends RulesActionBase implements ContainerFactoryPlugin
    +    // @todo pass a ReplicationTask to replicate(), but where would it come from?
    

    Should we just create an empty ReplicationTask to pass here?

  3. +++ b/src/ReplicatorInterface.php
    @@ -1,29 +1,42 @@
    +   * @todo should ReplicationTaskInterface be passed here as well?
    

    Is the ReplicationTask needed during the "applies" method logic? If so, we should pass it in as a parameter.

josephdpurcell’s picture

Status: Active » Needs review
timmillwood’s picture

Issue tags: +Needs tests

timmillwood’s picture

Status: Needs review » Needs work

Merged this, but still needs tests.

The last submitted patch, 3: 2749167_3.patch, failed testing.

The last submitted patch, 3: 2749167_3.patch, failed testing.

The last submitted patch, 3: 2749167_3.patch, failed testing.

josephdpurcell’s picture

I have created a test for the InternalReplicator, but it is failing: https://github.com/timmillwood/drupal-workspace/pull/6

My suspicion is that I am missing a step when triggering replication.

timmillwood’s picture

Can I suggest using BrowserTestBase like https://github.com/timmillwood/drupal-workspace/blob/8.x-1.x/tests/src/F... does?

The UID issue might be a deeper bug. We don't replicate users anymore, but instead try to match on UUID or username, I guess this is failing somewhere in the serializer.

josephdpurcell’s picture

Here is a patch showing the failing test. After switching to BrowserTestBase I experienced the same failure: while the entity was in fact replicated:

mysql> select nid,type,title,uid,workspace,_rev from simpletest717491node_field_data;
+-----+---------+---------------+-----+-----------+------------------------------------+
| nid | type    | title         | uid | workspace | _rev                               |
+-----+---------+---------------+-----+-----------+------------------------------------+
|   1 | article | Test Entity 1 |   2 |         3 | 1-729096abbe79b3a0f527ccbcaf090b42 |
|   2 | article | Test Entity 1 |   0 |         4 | 1-729096abbe79b3a0f527ccbcaf090b42 |
+-----+---------+---------------+-----+-----------+------------------------------------+
2 rows in set (0.00 sec)

The validation that replication was successful fails:

$node = Node::load($entity->id());
$this->assertEqual(FALSE, empty($node), 'Entity was found in target workspace');

I recall that there are caveats / difficulties to testing workspaces and replication (one reason for using BrowserTestBase) so I don't want to spend too much time on this test. As such I'm going to submit this for anyone to pick up and will come back to it (hopefully next week).

josephdpurcell’s picture

Patch #14 addresses this ticket and is related to #2764289: What action should Replication Filters apply to? and #2749177: Propose UI for Replication Filters.

The tests are passing on GitHub: https://github.com/timmillwood/drupal-workspace/pull/7

Here are key points of what this patch accomplishes:

  • Adds "push" and "pull" replication settings to the Workspace entity
  • Exposes replication settings to the user when editing a Workspace
  • Derives the "push" and "pull" replication settings where needed: WorkbenchModerationSubscriber, UpdateForm, ReplicateContent, and ReplicatorManager
  • Adds a test for the replication settings UI as well as their implementation: ReplicationSettingsTest

Potential improvements I see would be revising the UI to follow https://www.drupal.org/node/2764289#comment-11485043 and moving the majority of ReplicationSettings test to the replication module and having the test *just* be for the Workspace form setting the ReplicationSettings entity properly.

josephdpurcell’s picture

Status: Needs work » Needs review

The last submitted patch, 13: 2749167-test_13.patch, failed testing.

The last submitted patch, 13: 2749167-test_13.patch, failed testing.

The last submitted patch, 13: 2749167-test_13.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 14: 2749167_14.patch, failed testing.

The last submitted patch, 14: 2749167_14.patch, failed testing.

The last submitted patch, 14: 2749167_14.patch, failed testing.

phenaproxima’s picture

  1. +++ b/src/EventSubscriber/WorkbenchModerationSubscriber.php
    @@ -79,8 +80,16 @@ class WorkbenchModerationSubscriber implements EventSubscriberInterface {
    +    $replication_settings = $workspace->get('push_replication_settings')->referencedEntities();
    +    $replication_settings = count($replication_settings) > 0 ? reset($replication_settings) : NULL;
    +    if ($replication_settings !== NULL) {
    +      $task->setFilter($replication_settings->getFilterId());
    +      $task->setParametersByArray($replication_settings->getParameters());
    +    }
    

    Why have a $replication_settings flag? Why not simply do something like this:

    if (count($replication_settings) > 0) { $replication_settings[0] }
    
  2. +++ b/src/Form/UpdateForm.php
    @@ -89,9 +90,22 @@ class UpdateForm extends ConfirmFormBase {
    +      $replication_settings = $active
    +        ->getWorkspace()
    +        ->get('push_replication_settings')
    +        ->referencedEntities();
    +      $replication_settings = count($replication_settings) > 0 ? reset($replication_settings) : NULL;
    +      if ($replication_settings !== NULL) {
    +        $task->setFilter($replication_settings->getFilterId());
    +        $task->setParametersByArray($replication_settings->getParameters());
    +      }
    

    This is pretty close to what's in WorkbenchModerationSubscriber. Can it be moved into a utility method for DRY purposes?

  3. +++ b/src/Form/UpdateForm.php
    @@ -89,9 +90,22 @@ class UpdateForm extends ConfirmFormBase {
           $response = \Drupal::service('workspace.replicator_manager')->update(
             $upstream,
    -        $active
    +        $active,
    +        $task
    

    A bit out of scope, but the replicator_manager service should be injected if this is a form (FormBase implements ContainerInjectionInterface).

  4. +++ b/src/Plugin/RulesAction/ReplicateContent.php
    @@ -99,9 +100,17 @@ class ReplicateContent extends RulesActionBase implements ContainerFactoryPlugin
    +    $replication_settings = $source->get('push_replication_settings')->referencedEntities();
    +    $replication_settings = count($replication_settings) > 0 ? reset($replication_settings) : NULL;
    +    if ($replication_settings !== NULL) {
    +      $task->setFilter($replication_settings->getFilterId());
    +      $task->setParametersByArray($replication_settings->getParameters());
    +    }
    

    Again, this is repeating code. Can that be fixed?

  5. +++ b/src/ReplicatorManager.php
    @@ -52,14 +53,31 @@ class ReplicatorManager implements ReplicatorInterface {
    +    $replication_settings = $source
    +      ->getWorkspace()
    +      ->get('pull_replication_settings')
    +      ->referencedEntities();
    +    $replication_settings = count($replication_settings) > 0 ? reset($replication_settings) : NULL;
    +    if ($replication_settings !== NULL) {
    +      $pull_task->setFilter($replication_settings->getFilterId());
    +      $pull_task->setParametersByArray($replication_settings->getParameters());
    +    }
    

    Ditto here.

  6. +++ b/src/ReplicatorManager.php
    @@ -52,14 +53,31 @@ class ReplicatorManager implements ReplicatorInterface {
    +    $pull_log = $this->update($target, $source, $pull_task);
    +
    +    // @todo use $post_conflicts in a conflict management workflow
    

    How $pull_log used? It doesn't look like it's ever returned or touched further in this method.

jeqq’s picture

@josephdpurcell Tests are failing in Travis too: https://travis-ci.org/timmillwood/drupal-workspace/jobs/150725700

jeqq’s picture

@josephdpurcell Thank you for all your awesome work on this!

Addressed all point from #22.

@josephdpurcell Tests on github are failing because it's testing with 8.x-1.x branch for Replication module and this branch doesn't contain the replication_settings entity type from: https://github.com/relaxedws/drupal-replication/pull/12/files yet.

@josephdpurcell you can modify the drupal-8.1.x.make.yml and drupal-8.2.x.make.yml files (in https://github.com/timmillwood/drupal-workspace/pull/7) to test with your branch (from Replication).

@josephdpurcell Please commit changes from this patch to your branch on github: https://github.com/timmillwood/drupal-workspace/pull/7

Status: Needs review » Needs work

The last submitted patch, 24: support_replicationtask-2749167-24.patch, failed testing.

The last submitted patch, 24: support_replicationtask-2749167-24.patch, failed testing.

The last submitted patch, 24: support_replicationtask-2749167-24.patch, failed testing.

phenaproxima’s picture

Nice work, @jeqq. This is already a lot cleaner and more reusable. I have two points, though:

  1. +++ b/src/ReplicatorManager.php
    @@ -83,0 +77,22 @@
    +   * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace
    

    There's nothing about this method that flat-out /requires/ the replication settings source to be a workspace, so let's change this to EntityInterface, simply as a risk-free, pre-emptive defense against bizarre edge cases).

    Also, the parameter needs a description.

  2. +++ b/src/ReplicatorManager.php
    @@ -83,0 +77,22 @@
    +  public function getTask(WorkspaceInterface $workspace, $type) {
    

    I'd rather we passed the full field name in here, rather than the 'push' or 'pull' prefix -- just in case the field has a completely different name. Again, that's an edge case, but if it's easy for us to nip those in the bud then let's do that.

josephdpurcell’s picture

This patch uses #24 and addresses comments in #28.

@jeqq Thanks for the cleanup! And for finding the missing code on the replication module, I failed to create a ticket to address that.

I have opened #2781537: Add replication settings entity which is a dependency of this PR. For the time being I've modified the GitHub PR to use the branch for that ticket.

Status: Needs review » Needs work

The last submitted patch, 29: 2749167_29.patch, failed testing.

The last submitted patch, 29: 2749167_29.patch, failed testing.

The last submitted patch, 29: 2749167_29.patch, failed testing.

phenaproxima’s picture

This looks great! My only complaints are really just nit-scale.

  1. +++ b/src/EventSubscriber/WorkbenchModerationSubscriber.php
    @@ -108,11 +110,12 @@ class WorkbenchModerationSubscriber implements EventSubscriberInterface {
    +  static public function getSubscribedEvents() {
    

    Nit: For consistency, let's make this public static rather than static public :)

  2. +++ b/src/ReplicatorManager.php
    @@ -52,14 +55,49 @@ class ReplicatorManager implements ReplicatorInterface {
    +   * @param \Drupal\multiversion\Entity\EntityInterface $entity
    

    Should be \Drupal\Core\Entity\EntityInterface.

  3. +++ b/tests/src/Functional/ReplicationSettingsTest.php
    @@ -0,0 +1,187 @@
    +  /**
    +   * @var \Drupal\workspace\ReplicatorManager
    +   */
    +  protected $replicatorManager;
    

    Needs description.

  4. +++ b/tests/src/Functional/ReplicationSettingsTest.php
    @@ -0,0 +1,187 @@
    +  public static $modules = [
    

    Needs @inheritdoc.

  5. +++ b/tests/src/Functional/WorkspaceTestUtilities.php
    @@ -195,5 +195,18 @@ trait WorkspaceTestUtilities {
    +    $session = $this->getSession();
    +    $this->assertEquals(200, $session->getStatusCode());
    

    I think you can use $this->assertSession()->statusCodeEquals(200) here...

  6. +++ b/tests/src/Functional/WorkspaceTestUtilities.php
    @@ -195,5 +195,18 @@ trait WorkspaceTestUtilities {
    +    $page = $session->getPage();
    +    return $page->hasContent($label);
    

    This isn't an assertion; are you sure you don't want to use one? I think there's got to be something in $this->assertSession() which would work...

josephdpurcell’s picture

  1. Fixed.
  2. Whoops! Fixed
  3. Fixed.
  4. Fixed.
  5. 10 tests use this way of asserting the status code; for consistency's sake I'm going to leave as is unless it's sufficiently valuable to change all tests
  6. There are cases where you want to assert the presence or non-presence of a label; not using an assertion here allows the caller to decide
josephdpurcell’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 34: 2749167_34.patch, failed testing.

The last submitted patch, 34: 2749167_34.patch, failed testing.

The last submitted patch, 34: 2749167_34.patch, failed testing.

DamienMcKenna’s picture

@josephdpurcell: Please make sure to reroll the patch against the latest 8.x-1.x codebase, your patch doesn't apply.

josephdpurcell’s picture

phenaproxima’s picture

Issue tags: -Needs tests

As far as I'm concerned, this is RTBC.

DamienMcKenna’s picture

A minor change to use the correct replacement for assertResponse() (and instead of the assertEquals() calls that were being used).

Status: Needs review » Needs work

The last submitted patch, 42: workspace-n2749167-42.patch, failed testing.

The last submitted patch, 42: workspace-n2749167-42.patch, failed testing.

The last submitted patch, 42: workspace-n2749167-42.patch, failed testing.

DamienMcKenna’s picture

The test failures boil down to this one error:

Drupal\Component\Plugin\Exception\PluginNotFoundException: The "replication_settings" entity type does not exist.

Thoughts?

DamienMcKenna’s picture

So while comment #14 says "The tests are passing on GitHub: https://github.com/timmillwood/drupal-workspace/pull/7" we wouldn't want to commit a patch that would break every test run from then on.

DamienMcKenna’s picture

DamienMcKenna’s picture

Status: Needs work » Needs review

The last submitted patch, 40: 2749167_40.patch, failed testing.

The last submitted patch, 40: 2749167_40.patch, failed testing.

The last submitted patch, 40: 2749167_40.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 48: workspace-n2749167-48.patch, failed testing.

The last submitted patch, 48: workspace-n2749167-48.patch, failed testing.

The last submitted patch, 48: workspace-n2749167-48.patch, failed testing.

jeqq’s picture

@DamienMcKenna This will fail on d.o because this functionality depend on other new functionality in Replication module (it's not yet commited). See #24. Now we are testing this on github: https://github.com/timmillwood/drupal-workspace/pull/7

DamienMcKenna’s picture

Status: Needs work » Needs review

@jeqq: Oh, grr :( Hopefully the d.o repo will be synchronized soon.

So, lets ignore the patches in #48.

josephdpurcell’s picture

Status: Needs review » Needs work

The last submitted patch, 58: 2749167_58.patch, failed testing.

The last submitted patch, 58: 2749167_58.patch, failed testing.

The last submitted patch, 58: 2749167_58.patch, failed testing.

phenaproxima’s picture

I have one defensive concern and one nitpick, and once these are addressed this is RTBC.

  1. +++ b/src/ReplicatorManager.php
    @@ -52,14 +55,49 @@ class ReplicatorManager implements ReplicatorInterface {
    +    if ($items instanceof EntityReferenceFieldItemListInterface) {
    +      $referenced_entities = $items->referencedEntities();
    +      if (count($referenced_entities) > 0) {
    +        $task->setFilter($referenced_entities[0]->getFilterId());
    +        $task->setParametersByArray($referenced_entities[0]->getParameters());
    +      }
    +    }
    

    If $items is not valid, do we still want to return an empty ReplicationTask? It seems like we might want to fail with an exception here.

  2. +++ b/tests/src/Functional/ReplicationSettingsTest.php
    @@ -0,0 +1,191 @@
    +  /**
    +   * @var \Drupal\workspace\ReplicatorManager
    +   *   The replicator manager service used to trigger replication.
    +   */
    

    Nit: The description should come before @var and they should be separated by a blank line.

josephdpurcell’s picture

After some changes were made to #2781537: Add replication settings entity I believe this needs revisited. I will do so after final review on the #2781537: Add replication settings entity ticket is complete.

josephdpurcell’s picture

  1. Done. Good idea--not being able to get the task is a critical error made by the caller. Since we can't enforce it with a type error, an exception makes sense.
  2. Done. Again, I keep typing the property comment wrong!

Thanks for the great review--let's see if tests pass on github: https://travis-ci.org/timmillwood/drupal-workspace/builds/151668986

josephdpurcell’s picture

There was one test fail: https://travis-ci.org/timmillwood/drupal-workspace/jobs/151809113. However, it is unclear why. I've noticed that test is flaky on my local and all the other tests pass: https://travis-ci.org/timmillwood/drupal-workspace/builds/151809111

As such, perhaps those tests could be re-run or the one test failure ignored?

jeqq’s picture

@josephdpurcell the test is passing now.

josephdpurcell’s picture

Status: Needs work » Needs review

Great! Thanks for re-running it. I'll set this to needs review just so there's a record.

The last submitted patch, 64: 2749167_64.patch, failed testing.

The last submitted patch, 64: 2749167_64.patch, failed testing.

The last submitted patch, 64: 2749167_64.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 65: 2749167_65.patch, failed testing.

The last submitted patch, 65: 2749167_65.patch, failed testing.

The last submitted patch, 65: 2749167_65.patch, failed testing.

jeqq’s picture

I've tried to do manual testing. Push replication works fine but pull replication (update) fails with this error:

Notice: Undefined property: Drupal\workspace\Form\UpdateForm::$replicatorManager in Drupal\workspace\Form\UpdateForm->submitForm() (line 93 of /var/www/drupal8dev/modules/workspace/src/Form/UpdateForm.php)
josephdpurcell’s picture

The replicator manager service wasn't set. I also notice the wrong replication settings were populated. Both are fixed in this patch.

Status: Needs review » Needs work

The last submitted patch, 75: 2749167_75.patch, failed testing.

The last submitted patch, 75: 2749167_75.patch, failed testing.

The last submitted patch, 75: 2749167_75.patch, failed testing.

jeqq’s picture

+++ b/src/Form/UpdateForm.php
@@ -86,13 +86,14 @@ class UpdateForm extends ConfirmFormBase {
+    $replication_manager = \Drupal::service('workspace.replicator_manager');

I think replicator manager should be injected here, FormBase implements ContainerInjectionInterface.

josephdpurcell’s picture

I was following the pattern that existed. However, I agree--it should have dependencies injected.

I've refactored the form to have its dependencies injected.

phenaproxima’s picture

  1. +++ b/src/Form/UpdateForm.php
    @@ -7,21 +7,85 @@ use Drupal\Core\Ajax\CloseModalDialogCommand;
    +      $container->get('renderer')
    

    This is injected, and passed into the constructor, but $this->renderer is never actually set in __construct()...?

  2. +++ b/src/Form/UpdateForm.php
    @@ -102,16 +166,22 @@ class UpdateForm extends ConfirmFormBase {
        * @param array $form
    +   *   The form object.
    

    $form is not an object. :)

  3. +++ b/src/Form/UpdateForm.php
    @@ -102,16 +166,22 @@ class UpdateForm extends ConfirmFormBase {
    +   *   The ajax response.
    

    s/ajax/AJAX or Ajax

Status: Needs review » Needs work

The last submitted patch, 80: 2749167_80.patch, failed testing.

The last submitted patch, 80: 2749167_80.patch, failed testing.

The last submitted patch, 80: 2749167_80.patch, failed testing.

josephdpurcell’s picture

  1. Done. Whoops! Major mishap.
  2. Done. My subconcious mind wished it an object.
  3. Done. Good catch :)

Status: Needs review » Needs work

The last submitted patch, 85: 2749167_85.patch, failed testing.

The last submitted patch, 85: 2749167_85.patch, failed testing.

The last submitted patch, 85: 2749167_85.patch, failed testing.

phenaproxima’s picture

Status: Needs work » Reviewed & tested by the community

The patch looks great. I love it. Excellent work, @josephdpurcell!

The tests are only failing because this patch needs the HEAD of various other modules, but Drupal CI doesn't support that yet. As long as the tests pass on GitHub -- and they do -- we can march happily forward. It is my pleasure to mark this RTBC.

josephdpurcell’s picture

Removes the changes to the travis makefiles (.travis/drupal-8.1.x.make.yml and .travis/drupal-8.2.x.make.yml).

Leaving this as RTBC, however important reminder that this will fail without #2781537: Add replication settings entity.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 90: 2749167_90.patch, failed testing.

The last submitted patch, 90: 2749167_90.patch, failed testing.

The last submitted patch, 90: 2749167_90.patch, failed testing.

josephdpurcell’s picture

Status: Needs work » Reviewed & tested by the community

Setting back to RTBC per comments above.

jeqq’s picture

Status: Reviewed & tested by the community » Needs work
  1. I get this error when I try to install the module, seems that there are missing some references:
    PHP Fatal error:  Declaration of Drupal\workspace\Form\UpdateForm::create() must be compatible with Drupal\Core\DependencyInjection\ContainerInjectionInterface::create(Symfony\Component\DependencyInjection\ContainerInterface $container) in /var/www/drupal8dev/modules/workspace/src/Form/UpdateForm.php on line 205
    

    Tests in Travis are also failing because of this problem (some of them don't return the correct status): https://travis-ci.org/timmillwood/drupal-workspace/builds/152436700.

  2. +++ b/src/Form/UpdateForm.php
    @@ -7,21 +7,86 @@ use Drupal\Core\Ajax\CloseModalDialogCommand;
    +   *   Tthe replicator manager.
    

    A typo here.

I'll also do manual testing of the functionality.

Update:

After manual testing I've detected that replication doesn't work correctly when doing deploy (with Deploy module). The problem is in ReplicatorManager::replicate(), before doing push replication (this line: $push_log = $this->doReplication($source, $target, $task);) we should define the task if it is null. Something like this:

    if ($task === NULL) {
      // Derive a replication task from the source Workspace for pushing.
      $task = $this->getTask($source->getWorkspace(), 'push_replication_settings');
    }

    // Push changes from $source to $target.
    $push_log = $this->doReplication($source, $target, $task);
josephdpurcell’s picture

  1. Done. I was missing a use statement.
  2. Done.

I suspect that the manual testing failed from the module not properly getting installed. The logic *should* properly handle the case where a $task does not exist. I tested it manually on my local and it seems to function.

Would you mind validating it works for you as well?

Status: Needs review » Needs work

The last submitted patch, 96: 2749167_96.patch, failed testing.

The last submitted patch, 96: 2749167_96.patch, failed testing.

The last submitted patch, 96: 2749167_96.patch, failed testing.

josephdpurcell’s picture

Adding additional missing use statements.

Status: Needs review » Needs work

The last submitted patch, 100: 2749167_100.patch, failed testing.

The last submitted patch, 100: 2749167_100.patch, failed testing.

The last submitted patch, 100: 2749167_100.patch, failed testing.

jeqq’s picture

@josephdpurcell I've missed your last message from #96 yesterday. When I tested it, the push replication didn't work. For example when doing it like this (an example from Deploy module ReplicationForm::save()):

\Drupal::service('workspace.replicator_manager')->replicate($source, $target);

That because the task is null in this case. It looks obvious to me because we should check for replication settings both on pull and push replication (we do both replication types in ReplicatorManager::replicate(), but check for settings just on pull). If the task is null, in InternalReplicator::replicate() it defines a new task without replication setting.

My solution from #95 fixes this. Does this make sense?

josephdpurcell’s picture

@jeqq I've been in the weeds of this for a while, so thank you for giving this a thoughtful look, but also please have patience--it will be easy for me to miss the obvious (Linus' Law is a blessing and a curse!).

I do not see the issue, unfortunately. I will attempt to clarify my perspective with the hope you can identify the flaw.

General commentary

High level goal: this implementation should be backwards compatible with any other module, e.g. Deploy or Relaxed.

The primary way to achieve that was by making the added parameter optional like so:

  public function replicate(WorkspacePointerInterface $source, WorkspacePointerInterface $target, ReplicationTaskInterface $task = NULL);

So, the example from Deploy module, ReplicationForm::save(), should execute without alteration of behavior.

More specific commentary

In ReplicatorManager::replicate there is a "pull" replication as well as a "push" replication.

pull: The objective of the pull is to ensure there's no conflicts, hence these lines:

    // Derive a replication task from the target Workspace for pulling.
    $pull_task = $this->getTask($source->getWorkspace(), 'pull_replication_settings');

    // Pull in changes from $target to $source to ensure a merge will complete.
    $this->update($target, $source, $pull_task);

Assuming Deploy has a dependency on Workspace module, the above code should execute without alteration of Deploy's behavior. The getTask() call should return an instance of ReplicationTask without alteration (similar to what we see line 108 of InternalReplicator::replicate). The purpose of using an empty ReplicationTask object is to provide default values. There are other patterns for handling defaults, but I like the idea of using the default property values of a value object as the defaults.

push: The push replication step should also execute without alteration. Assuming NULL is passed in for the $task parameter, that should get passed on to the InternalReplicator (or other ReplicatorInterface) and the replicator should use default values.

Conclusion

I've tested this patch manually with Deploy, Workspace, Workbench Moderation, Replication, and Multiversion installed and Deploy appears to work correctly:

  1. Create a Workspace with upstream set to "Live", do not set any replication settings
  2. Switch to this new workspace and create an entity
  3. Create a deployment and save
  4. Validate the entity was replicated to live by switching to the live workspace and looking at the list of content

As I type this, however, I realize Workbench Moderation is not required--so I'll re-test this without that module.

It would be worthwhile to also validate that Relaxed works correctly with this patch. If I have time I will test with that.

josephdpurcell’s picture

I tested what I described in #105 without Workbench Moderation installed and the deployment succeeded.

@jeqq For next steps, I think the most expedient path would be to submit a patch with the changes you see required or make a comment on the lines of code that should change and I would be happy to make those changes--doing this would give concrete code changes we can discuss.

jeqq’s picture

@josephdpurcell I've described all steps to reproduce the problem and attached the patch with the proposed solution.

  1. Clean Drupal install (8.2.x)
  2. Apply this patch for Workspace
  3. Enable Deploy module (dev versions for all dependencies).
  4. Go to Live workspace edit page (admin/structure/workspace/1/edit):
  • Assign default target workspace: Stage
  • Replication settings on update: Replicate only page entities
  • Replication settings on deploy: Replicate only article entities
  • Save
  1. Go to Stage workspace edit page (admin/structure/workspace/2/edit), keep defaults:
  • Assign default target workspace: Live
  • Replication settings on update: None
  • Replication settings on deploy: None
  • Save
  1. On Live workspace add one article and one page.
  2. On Stage workspace add one article and one page.
  3. Go on Live workspace:
  • Click on Update link (top menu right): Confirm that you want to pull changes from Stage to Live.
  • Make sure you are on Live workspace, go to /admin/content - you should see one article and two pages (one page has been pulled from Stage) - all fine.
  • Make sure you are on Live workspace, click on Deploy link (top menu right): Add a title for the new deploy in the opened popup and click Deploy to Stage. After that you should see confirmation message: Successful deployment.
  • Make sure you are on Live workspace, go to /admin/content - you should see one article and two pages - all fine.
  1. Go on Stage workspace:
  • Go to /admin/content - here, theoretically, you should have one page and two articles (one from Stage and one from Live), but, actually we have here just one article and one page - the page from Live haven’t got replicated.
  • Make sure you are on Stage workspace, click on Update link (top menu right): Confirm that you want to pull changes from Live to Stage.
  • Make sure you are on Stage workspace, go to /admin/content - you should see two articles and two pages - all fine.
  • Make sure you are on Stage workspace, create one new page and one new article.
  • Make sure you are on Stage workspace, click on Deploy link (top menu right): Add a title for the new deploy in the opened popup and click Deploy to Live. After that you should see confirmation message: Successful deployment.
  • Go on Live workspace, go to /admin/content - you should see three articles and three pages - all fine.
  1. Go on Live workspace (try to reproduce again the problem):
  • Create one new page and one new article.
  • Make sure you are on Live workspace, click on Deploy link (top menu right): Add a title for the new deploy in the opened popup and click Deploy to Stage. After that you should see confirmation message: Successful deployment.
  • Go on Stage workspace, go to /admin/content - theoretically, you should have 3 pages and 4 articles, but, actually we have here just 3 articles and 3 page - the page from Live haven’t got replicated.

Conclusion: When a workspace have Replication settings on deploy filter set, the push replication doesn’t work (we have the Live workspace in the previous example). For Stage workspace push replication works fine because it doesn’t have a Replication settings on deploy filter set.

Status: Needs review » Needs work

The last submitted patch, 107: support_replicationtask-2749167-107.patch, failed testing.

The last submitted patch, 107: support_replicationtask-2749167-107.patch, failed testing.

The last submitted patch, 107: support_replicationtask-2749167-107.patch, failed testing.

josephdpurcell’s picture

Ah! It's extremely obvious to me now what the problem is.

The ReplicatorManager::replicate assumes the $task that will be used will get passed to the method. However, Deploy does not do that. So, the question is, how should Workspace respond? @jeqq your suggestion is Workspace should derive the task for Deploy.

At least in my own mind, a Pandora's box has been opened. I have more questions now about how Workspace is designed and whether we've created two inconsistencies in the UI: one for how replication is triggered and one for how replication settings are defined/configured.

Deploy, Workspace, Workbench Moderation, Relaxed UX Conflict

Deploy:

  • Triggers a replication through a form submission.
  • No configuration yet for that replication.
  • workspace\InternalReplicator is used via workspace\ReplicatorManager

Workspace (with Workbench Moderation):

  • Triggers a replication through state transition of a Workspace to a published state.
  • Configuration of replication settings on the Workspace.
  • workspace\InternalReplicator is used via workspace\ReplicatorManager

Relaxed (with Workbench Moderation):

  • Same code triggering replication as Workspace (with Workbench Moderation)
  • Same configuration as Workspace (with Workbench Moderation)
  • relaxed\CouchdbReplicator is used via workspace\ReplicatorManager

Relaxed (with Deploy):

  • Same code triggering replication as Deploy
  • Same configuration as Deploy
  • workspace\InternalReplicator is used via workspace\ReplicatorManager because workspace\InternalReplicator has priority 10 and relaxed\CouchdbReplicator has priority 20

Commentary:

  • Triggering replication: The only modules that define a UI for triggering a replication are Deploy and Workspace (with Workbench Moderation). It seems to me the only reason Deploy depends on Workspace is for the replication plumbing. By moving the replication plumbing to the Replication module we could eliminate Deploy's dependency on Workspace and allow each module to define the UI for triggering a replication completely independent of each other.
  • Configuration of replication: Deploy, Workspace (with Workbench Moderation), and Relaxed each have separate ways they may want to define replication settings:
    • Deploy: I could see the configuration existing on the Workspace OR on the deployment entity.
    • Workspace (with Workbench Moderation): per this patch the settings are on the Workspace itself.
    • Relaxed: I could see the configuration existing on the Workspace OR the relaxed endpoint.

    I see two approaches: (1) allow each module to provide the way in which replication settings are derived for the replication, or (2) have the replication settings always derived from the Workspace. Right now it's hybrid: you can pass in a task or have it derived--I think it would be good to choose one approach or the other and refactor ReplicatorManager to comply with it: in the case of (1) refactor ReplicatorManager to know nothing about replication settings being on workspaces and force the push and pull replication settings be passed in, or (2) refactor ReplicatorManager to not allow passing in a replication task and just derive it at will.

I'm going to hold off on proceeding with this ticket until I've had a more in depth conversation on how this should work for the user.

JamesK’s picture

Deploy doesn't make sense without Workspace. IMO Deploy-triggered replications should use the Workspace replication settings.

josephdpurcell’s picture

After discussing this with @phenaproxima and @dixon_ we're going to take the pragmatic route of using a conditional as @jeqq suggested: this means that any module using Workspace should assume the replication settings come from the Workspace.

This patch uses #107 but also adds some comments and changes the Workspace that is used to derive the replication settings: they should always come from the Workspace being replicated to, i.e. the "target" Workspace.

For the longer term, the focus will be two things:

  1. Ensure there is good coverage of functional testing (web tests); this will be of benefit if the module is refactored
  2. Identify any major assumptions the module is making that should be refactored out into Replication module

I don't have time to address these two points at present but will do so this week.

Status: Needs review » Needs work

The last submitted patch, 113: 2749167_113.patch, failed testing.

The last submitted patch, 113: 2749167_113.patch, failed testing.

The last submitted patch, 113: 2749167_113.patch, failed testing.

jeqq’s picture

@josephdpurcell These changes look wrong to me:

  1. +++ b/src/ReplicatorManager.php
    @@ -64,7 +64,7 @@ class ReplicatorManager implements ReplicatorInterface {
    +    $pull_task = $this->getTask($target->getWorkspace(), 'pull_replication_settings');
    
         // Pull in changes from $target to $source to ensure a merge will complete.
         $this->update($target, $source, $pull_task);
    

    In this case content will be replicated from $target workspace to $source workspace, so, $source is the target. Setting should come from $source.

  2. +++ b/src/ReplicatorManager.php
    @@ -72,9 +72,12 @@ class ReplicatorManager implements ReplicatorInterface {
    +      $task = $this->getTask($target->getWorkspace(), 'push_replication_settings');
    

    Logically, on push replication, settings should come from $source workspace, otherwise push replication settings doesn't make sense.

josephdpurcell’s picture

@jeqq The confusion is warranted, imo. I think the DX here can be improved to clarify what should happen.

I switched them back--you're absolutely right. The task should be derived on the workspace being acted on, most often this is the live workspace.

I also did a few code cleanups.

Status: Needs review » Needs work

The last submitted patch, 118: 2749167_118.patch, failed testing.

The last submitted patch, 118: 2749167_118.patch, failed testing.

The last submitted patch, 118: 2749167_118.patch, failed testing.

jeqq’s picture

Assigned: josephdpurcell » Unassigned
Status: Needs work » Closed (fixed)

Commited: http://cgit.drupalcode.org/workspace/commit/?id=35fdcf8

Thank you @josephdpurcell and @phenaproxima!