When applying a recipe, we need to trigger an event pre importing content

Created on 4 December 2024, 4 months ago

Problem/Motivation

When a recipe is imported, there is no way of being aware that we are importing content.

@pameeela found this with the Drupal CMS recipes. If a recipe imports content (e.g. drupal_cms_privacy_basic), and another recipe depends on this one (e.g. drupal_cms_events), it will run the import content again.

This is not a problem unless the content has been trashed, when there is a uuid duplicated error. Trash alters the content storage by adding "trash contexts", as the "active" trash context which will exclude any trashed content.

If we had an event, Trash could ensure to activate the "ignore" context, as it already does with workspaces on pre/post-publish.

If we don't do this, drupal_cms recipes that have dependants can't provide default content.
This could affect any other site using trash module, not only Drupal CMS.

I'm also assuming that any default_content that isn't revisionable will suffer of being overridden on the second import if it had edits. So this wouldn't only affect trash. I didn't verify this scenario though.

Steps to reproduce

- Install drupal_cms (that applies drupal_cms_privacy_basic recipe)
- Delete the Privacy Policy node.
- ddev recipe-apply recipes/drupal_cms_privacy_basic (that tries to apply the same recipe again)

Crashes with:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '00d105b3-6f05-40c6-a289-3dd61c89480e' for key 'node_field__uuid__value': INSERT INTO "node" ("vid", "type", "uuid", "langcode") VALUE

Proposed resolution

When applying a recipe, we need to trigger an event pre importing content.
This might as well be a responsability of default_content itself and not the RecipeRunner.

Remaining tasks

MR

User interface changes

None.

Introduced terminology

None.

API changes

New event

Data model changes

None.

Release notes snippet

TBD

Feature request
Status

Active

Version

11.0 🔥

Component

default content system

Created by

🇪🇸Spain penyaskito Seville 💃, Spain 🇪🇸, UTC+2 🇪🇺

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @penyaskito
  • 🇪🇸Spain penyaskito Seville 💃, Spain 🇪🇸, UTC+2 🇪🇺
  • 🇪🇸Spain penyaskito Seville 💃, Spain 🇪🇸, UTC+2 🇪🇺
  • Pipeline finished with Failed
    4 months ago
    Total: 126s
    #359704
  • Pipeline finished with Success
    4 months ago
    Total: 2126s
    #359736
  • 🇪🇸Spain penyaskito Seville 💃, Spain 🇪🇸, UTC+2 🇪🇺

    @alexpott We don't recreate it: $importer->importContent($content, Existing::Skip);
    We are just failing to detect it's existing. That's why the trash context is relevant.

  • 🇮🇹Italy gambry Milan

    Hi, Gab from Drupalcon Signapore Contribution Day. Looking at this.

  • 🇮🇹Italy gambry Milan

    TL;DR: I'm with current changes without alteration.

    I spent quite a while, this is my understanding:

    The original aim of the changes proposed is to allow subscribers to react by altering application states, while not changing the content. This seems a reasonable no-disruptive approach, since any default content provided will be processed as expected by their providers.

    Passing $existing
    Passing "Existing" is harmless per-se, but it will require splitting the loop in two parts and dispatching this event after the first loop loading the entities. Or similar approaches.
    The benefit of having $existing is to let event subscribers react when something exists.

    IMO the benefits don't pay back for the work that needs to be done. Subscribers can anyway load the entities by themselves and checking if they exist. Loading entities will be cached so when re-run by the Importer class it will be seamless.

    Allowing subscribers to remove items
    Subscriber will be able to remove items from the list (let's not give the chance to alter content too 🙏). It won't require many changes and it could be done in a data-integrity safe way.

    However if I'm a recipe owner I want to ultimately be responsible for my content. I expect for it to be imported and if there is a failure I'm ultimatelly responsible for fixing it.
    If I'm a contrib module owner, and my module weirdly handle content I should be ultimatelly responsible to fix my 💩 (like Trash module is trying to do).

    Allowing any subscribers to manipulate the list without notifying the rest of the subscribers - which would require a new event :D - seems dangerous to me.

    Conclusion
    For this reason I believe the current implementation is the most obvious, covering the majority of usecases and reducing risks of overcomplicating the import flow.

  • 🇮🇹Italy gambry Milan

    @nicxvan I don't know. What's the policy in this scenario?

  • 🇮🇹Italy gambry Milan

    Moving this back to Needs Work for passing the $existing intent together with $content.

  • 🇺🇸United States phenaproxima Massachusetts

    This is not a Drupal CMS stable blocker. It would be very nice to have it, and hopefully it can land in a patch release of 11.1.x as a change in an experimental subsystem, but it does not block a stable release of Drupal CMS. Getting this would, however, allow us to avoid a nasty bug that is relatively edge-casey, so keeping it as a release target in case this lands in a core patch release before Drupal CMS's general launch.

  • Pipeline finished with Canceled
    4 months ago
    Total: 305s
    #371500
  • Pipeline finished with Failed
    4 months ago
    Total: 170s
    #371505
  • 🇺🇸United States phenaproxima Massachusetts

    Went ahead and implemented the skip mechanism.

  • Pipeline finished with Success
    4 months ago
    Total: 3307s
    #371518
  • Pipeline finished with Canceled
    4 months ago
    Total: 130s
    #371683
  • Pipeline finished with Canceled
    4 months ago
    Total: 137s
    #371685
  • Pipeline finished with Success
    4 months ago
    Total: 381s
    #371688
  • First commit to issue fork.
  • Pipeline finished with Success
    4 months ago
    Total: 397s
    #371707
  • 🇺🇸United States thejimbirch Cape Cod, Massachusetts

    Marking as RTBC. Love to see some more improvement in the default content system!

  • 🇺🇸United States phenaproxima Massachusetts

    I would advocate to do that in a follow-up.

  • 🇷🇴Romania amateescu

    Trash could use that to say that an entity was skipped because one with the same UUID is in the trash bin.

  • 🇺🇸United States phenaproxima Massachusetts

    Hmmm...yeah, okay @amateescu, you've convinced me that we should support filing a reason why the skip happened (the reason should be optional). Trash's use case makes perfect sense.

  • Pipeline finished with Success
    4 months ago
    Total: 496s
    #372485
  • 🇺🇸United States phenaproxima Massachusetts

    And, done.

  • 🇷🇴Romania amateescu

    Looks good to me, thanks @phenaproxima! Left a comment on the MR, feel free to close it if you disagree :)

  • Pipeline finished with Failed
    4 months ago
    Total: 1964s
    #372498
  • 🇬🇧United Kingdom alexpott 🇪🇺🌍

    Discussed #8 with @phenaproxima we agreed that in this situation the event is offering benefits - the event object collects skips and validates them. This type of collecting and validating with an object has not been done with hooks yet and there is no need for this to be first.

    Also discussed with @longwave ahile back and we agreed to backport this to 11.1.x because while this is not a bug in core it helps fix a bug possible when you have trash installed - which Drupal CMS does.

    Committed and pushed 51ae03891c1 to 11.x and c813f52a5be to 11.1.x. Thanks!

    • alexpott committed c813f52a on 11.1.x
      Issue #3491782 by phenaproxima, penyaskito, thejimbirch, gambry,...
    • alexpott committed 51ae0389 on 11.x
      Issue #3491782 by phenaproxima, penyaskito, thejimbirch, gambry,...
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024