Skip to content

Peculiar behavior with the community submission flow and curation checks #2247

@bressler95tops

Description

@bressler95tops

It it worth noting that I am working from a Zenodo fork based on invenio-app-rdm v14 dev4. I also opened a similar ticket in their repository in case this is something specific to Zenodo that changes the behavior of community submissions but there is nothing obvious that indicates this with the dependency tree. The extension version is invenio-rdm-records v22.7.1.

Describe the bug

A brief description of this bug is that my curation checks, which were created using an invenio shell script that I wrote to create them. It doesn't feel like this code would cause the issues I am seeing, but perhaps you may see something there that does have something to do with it. I will provide that in the "Additional Context" section. But essentially, there seems to be a problem with curations checks and submitting to communities. When a curation check is not fulfilled (as you will see in the steps), saving a draft results in a white screen with a React error.

Steps to Reproduce

  1. When I go to edit the record for an already submitted record:
Image
  1. And I re-save as so with one of the curation checks purposely unfulfilled (in this case it is checking if the description contains WORD, so we exclude it as a test). This seems to intermittently be the case when I start a new record in that community and click the "Save Draft" button, I suspect because I might have to something from the form validation and the subsequent likewise can be treated as alternative step 2 to replicate this:
Image
  1. I get this blank screen with just the header and footer, and the header of the community. There is nothing below it when normally the form would be there, or the tabbed interface that shows curation checks but in my case nothing shows once I click "Save Draft".
Image

The browser console shows Error: Minified React error #31 which contains Objects are not valid as a React child (found: object with keys {message, severity, description}). If you meant to render a collection of children, use an array instead.

  1. When I make sure the condition is met and I re-save the draft, it proceeds without that error and shows the checks view as expected.
Image
  1. Likewise, you can repeat this, even when the previous save attempt fulfilled the curation check conditions.

Expected behavior

For drafts to save on communities submissions without the screen going blank (and showing a React error) regardless of whether checks are fulfilled.

Screenshots

I have screenshots in my "Steps to Reproduce" section.

Additional context (What I've Tried)

Specifically, the message, severity, description that the React error is indicating.

I have done some debugging and modified the deposit.js in a fork of invenio-rdm-records (/assets/semantic-ui/js/invenio_rdm_records/src/deposit/actions/deposit.js). I am not completely sure what the originating cause may be but I followed this approach:

Sanitizing the Data

These changes are what seemingly "fixed" the behavior that I described. I am still wrapping my head around about the reason for the behavior in the first place.

The full differential on my fork

I can open a pull request to your repository if you think there's anything worth merging, although I would be interested in addressing it in another way if you think there's a better one.

 const clean = (obj) => {
    if (!obj || typeof obj !== "object") return obj;
    for (const key in obj) {
      const v = obj[key];
      // If we find the {message, severity, description} object:
      if (v?.message && typeof v === "object") {
        obj[key] = v.message;
      } else if (typeof v === "object") {
        clean(v); // Search deeper
      }
    }
    return obj;
  };

  // Clean both the direct errors and any errors embedded in data
  response.errors = clean(response.errors);
  if (response.data?.errors) {
    response.data.errors = clean(response.data.errors);
  }

Set Up Debugging

You will notice I hooked in a variable getState for some of the functions to attempt to identify differences in that data, which is difficult to compare with how much is in the structure. I could write a comparison function but I took a chance on my cleaner function which surprisingly "fixed" the problem.

Validation Messages in UI

Interestingly, the validation on my curation check enabled fields always showed an amiguous message at the top of the page that didn't explain to the user what they had to change. With my changes, the detailed validation message shows when saving the draft, which is more desirable behavior.

Image

The Curation Code

Like I mentioned, I wrote my own custom code to insert my curation checks into my communities. This may provide some context on whether something I did here is the cause and not the invenio-rdm-records handling of payload data. It does a little bit more than just setup my curation code, but that isn't relevant to what is described here.

setup-curation-checks.py

I run this using invenio shell


import sys
import time
from invenio_communities.communities.records.models import CommunityFeatured
from invenio_communities.communities.records.api import Community
from invenio_communities.proxies import current_communities 
from invenio_checks.models import CheckConfig, Severity
from invenio_db import db

# For the rr team working on this, it seems invenio-checks uses this logic for metadata check levels that don't line up with the documentation
# Refer to https://github.com/inveniosoftware/invenio-checks/blob/master/invenio_checks/templates/semantic-ui/invenio_checks/requests/metadata_tab.html

TARGET_SLUGS = [
    "astrophysics",
    "biophys-science",
    "earth-science",
    "heliophysics",
    "planetary-science"
]

METADATA_PARAMS = {
    "title": "Required Metadata",
    "description": "Metadata requirements.",
    "rules": [
        {
            "id": "check-title",
            "title": "Title Required",
            "message": "Title is required.",
            "description": "Title is required.",
            "level": "error",
            "checks": [
                {
                    "type": "field", 
                    "path": "metadata.title", 
                    "operator": "exists"
                }
            ]
        },
        {
            "id": "require-description",
            "title": "Description Missing",
            "message": "A description is required for this record.",
            "level": "error",
            "checks": [
                {
                    "type": "field", 
                    "path": "metadata.description", 
                    "operator": "exists"
                }
            ]
        },
        {
            "id": "check-in-description",
            "title": "WORD in Description",
            "message": "The word WORD is required in the description field for this record.",
            "description": "The word WORD is required in the description field for this record.",
            "level": "info",
            "checks": [
                {
                    "type": "comparison",
                    "left": {
                        "type": "field",
                        "path": "metadata.description"
                    },
                    "operator": "~=", 
                    "right": "WORD"
                }
            ]
        }
    ]
}

def resolve_uuid(slug, max_retries=30, delay=5):
    """
    Tries to resolve the community UUID.
    If not found, waits 'delay' seconds and tries again.
    """
    for attempt in range(1, max_retries + 1):
        try:
            community = Community.pid.resolve(slug)
            return community.id
        except Exception:
            print(f"   ⏳ Attempt {attempt}/{max_retries}: '{slug}' not ready yet... waiting {delay}s")
            time.sleep(delay)
    
    return None

def configure_community(slug):
    print(f"\n--- PROCESSING: {slug} ---")

    # 1. RESOLVE UUID
    uuid = resolve_uuid(slug)
    
    if not uuid:
        print(f"   ❌ TIMEOUT: Could not find '{slug}'. Skipping.")
        return

    print(f"   ✅ Found UUID: {uuid}")

    # 2. FEATURE COMMUNITY LOGIC
    try:
        is_featured = CommunityFeatured.query.filter_by(community_id=uuid).first()
        
        if not is_featured:
            featured_entry = CommunityFeatured(
                community_id=uuid,
                start_date=None
            )
            db.session.add(featured_entry)
            print("   -> Community marked as Featured in DB.")
        else:
            print("   -> Already featured in DB. Skipping.")
    except Exception as e:
        print(f"   ❌ ERROR during featuring: {e}")

    # 3. APPLY CURATION CHECKS
    try:
        # Metadata Check
        existing_meta = CheckConfig.query.filter_by(community_id=uuid, check_id="metadata").first()
        if not existing_meta:
            config_meta = CheckConfig(
                community_id=uuid,
                check_id="metadata",
                params=METADATA_PARAMS,
                severity=Severity.WARN,
                enabled=True,
            )
            db.session.add(config_meta)
            print("   -> Metadata check staged.")
        else:
            # UPDATE EXISTING
            # We explicitly re-assign the params to ensure DB picks up changes to the JSON
            existing_meta.params = METADATA_PARAMS
            existing_meta.severity = Severity.WARN
            existing_meta.enabled = True
            print("   -> Metadata check updated.")

        # File Formats Check
        existing_files = CheckConfig.query.filter_by(community_id=uuid, check_id="file_formats").first()
        if not existing_files:
            config_files = CheckConfig(
                community_id=uuid,
                check_id="file_formats",
                params={},
                severity=Severity.WARN,
                enabled=True,
            )
            db.session.add(config_files)
            print("   -> File check staged.")
        else:
            # UPDATE EXISTING
            existing_files.params = {}
            existing_files.severity = Severity.WARN
            existing_files.enabled = True
            print("   -> File check updated.")

        # Commit DB changes
        db.session.commit()
        
        # 4. TRIGGER SEARCH INDEX REFRESH
        # Essential for the community to appear in the homepage slider
        current_communities.service.indexer.index_by_id(uuid)
        print("   -> Search index updated.")
        
    except Exception as e:
        db.session.rollback()
        print(f"   ❌ ERROR configuring curation for '{slug}': {e}")

def run():
    print("Starting Community Configuration...")
    
    for slug in TARGET_SLUGS:
        configure_community(slug)
    
    print("\n--- DONE ---")

if __name__ == "__main__":
    run()

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions