Skip to content

Remove Hardcoded Node Type Validation - Enable Schema-Based Type Discovery #446

Description

@malibio

Parent Issue

Part of Epic #445: Custom Entity Types - User-Defined Schemas with Natural Language Creation

Problem

The backend currently uses a hardcoded ALLOWED_NODE_TYPES constant that blocks creation of custom entity types:

Current validation (packages/desktop-app/src-tauri/src/constants.rs):

pub const ALLOWED_NODE_TYPES: &[&str] = &[
    "text", "header", "task", "date", "code-block", "quote-block", "ordered-list"
];

This prevents users from creating nodes with custom types like "invoice", "customer", "training-session", etc., even though:

  • The database schema supports any string as node_type
  • The schema system can define validation for any type
  • The frontend plugin system supports dynamic types

Solution

Replace hardcoded validation with schema-based validation:

Before:

fn validate_node_type(node_type: &str) -> Result<(), String> {
    if !ALLOWED_NODE_TYPES.contains(&node_type) {
        return Err(format!("Only text, task, and date nodes are supported. Got: {}", node_type));
    }
    Ok(())
}

After:

async fn validate_node_type(node_type: &str, schema_service: &SchemaService) -> Result<(), NodeServiceError> {
    // Check if schema exists for this type
    match schema_service.get_schema(node_type).await {
        Ok(_) => Ok(()),
        Err(_) => Err(NodeServiceError::invalid_update(
            format!("No schema found for node type: {}. Create a schema first.", node_type)
        ))
    }
}

Implementation Details

Files to Modify

1. Remove constant (packages/desktop-app/src-tauri/src/constants.rs):

  • Delete ALLOWED_NODE_TYPES constant entirely
  • Update any documentation referencing this constant

2. Update validation (packages/desktop-app/src-tauri/src/commands/nodes.rs):

  • Replace ALLOWED_NODE_TYPES.contains() checks with schema lookups
  • Make validation function async (requires schema service)
  • Update function signatures to accept SchemaService reference
  • Used in: create_node, create_container_node, update_node, save_node_with_parent

3. Update error messages:

  • Change from "Only text, task, and date nodes are supported"
  • Change to "No schema found for node type: {}. Create a schema first."
  • Add helpful guidance about creating schemas

SchemaService Integration

The schema service already exists at packages/core/src/services/schema_service.rs:

pub async fn get_schema(&self, schema_id: &str) -> Result<SchemaDefinition, NodeServiceError> {
    let node = self.node_service.get_node(schema_id).await?;
    if node.node_type != "schema" {
        return Err(NodeServiceError::invalid_update(...));
    }
    let schema: SchemaDefinition = serde_json::from_value(node.properties)?;
    Ok(schema)
}

Integration approach:

// In nodes.rs command handlers
#[tauri::command]
pub async fn create_node(
    state: State<'_, AppState>,
    node_type: String,
    // ... other params
) -> Result<Node, String> {
    // Validate type against schema
    validate_node_type(&node_type, &state.schema_service).await
        .map_err(|e| e.to_string())?;
    
    // Continue with node creation...
}

Backward Compatibility

Core types (text, task, date, etc.) already have schemas, so validation will pass for them automatically. This change is fully backward compatible.

Acceptance Criteria

Functional Requirements

  • Backend accepts any node type that has a corresponding schema
  • Backend rejects node types without schemas with clear error message
  • All core types (text, task, date, etc.) continue to work
  • Custom entity types work after schema creation
  • Error messages guide users to create schemas

Technical Requirements

  • ALLOWED_NODE_TYPES constant removed from codebase
  • All validation functions updated to use schema service
  • Validation is async and properly awaited
  • SchemaService injected into command handlers
  • No performance degradation (<10ms validation overhead)

Testing Requirements

  • Unit tests: Schema validation for existing types
  • Unit tests: Schema validation rejects missing schemas
  • Unit tests: Error messages are clear and actionable
  • Integration tests: Create node with custom type after schema creation
  • Integration tests: Reject node creation without schema
  • Regression tests: All core types still work

Testing Plan

Unit Tests (Rust)

#[tokio::test]
async fn test_validate_existing_schema() {
    let schema_service = setup_test_schema_service();
    schema_service.create_schema("invoice", fields).await.unwrap();
    
    let result = validate_node_type("invoice", &schema_service).await;
    assert!(result.is_ok());
}

#[tokio::test]
async fn test_validate_missing_schema() {
    let schema_service = setup_test_schema_service();
    
    let result = validate_node_type("nonexistent", &schema_service).await;
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("No schema found"));
}

#[tokio::test]
async fn test_core_types_still_work() {
    let schema_service = setup_test_schema_service();
    
    for core_type in ["text", "task", "date"] {
        let result = validate_node_type(core_type, &schema_service).await;
        assert!(result.is_ok(), "Core type {} should be valid", core_type);
    }
}

Integration Tests

  • Create custom schema via MCP/Tauri command
  • Create node with custom type
  • Verify node creation succeeds
  • Verify properties are stored correctly

Migration Notes

No database migration required - This is purely a validation change. The database schema already supports any string as node_type.

Deployment notes:

  • Core schemas must exist before deployment (already present)
  • No data migration or backfill needed
  • Can be deployed without downtime

Performance Considerations

Schema lookup adds ~1-5ms per validation:

  • Acceptable for CRUD operations
  • SchemaService can cache schemas in memory
  • Future optimization: Cache validation results per type

Optimization opportunity (future enhancement):

struct SchemaValidationCache {
    validated_types: HashSet<String>,
    last_refresh: Instant,
}

Related Issues

Documentation Updates

  • Update API documentation for node creation endpoints
  • Add schema validation section to developer guide
  • Document error codes and troubleshooting
  • Update architecture docs with new validation flow

Success Metrics

  • Zero regressions in core type creation
  • Custom types work immediately after schema creation
  • Error messages result in <5 support requests
  • Validation overhead <10ms (measured via benchmarks)

Labels: foundation, backend, breaking-change-internal, rust
Priority: High (blocks custom entity feature)
Estimated effort: 4-6 hours

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions