diff --git a/ci/config.yaml b/ci/config.yaml new file mode 100644 index 00000000..3146a926 --- /dev/null +++ b/ci/config.yaml @@ -0,0 +1,57 @@ +# V3 Schema Configuration +# This file defines valid enum values for schema fields +# Validation rules and logic live in validate_v3.py + +version: 3 + +# Valid availability states for plugins and connectors +availability: + - IDEA + - VALIDATED + - BUILT_IN + - INSTALLABLE + - IMPOSSIBLE + +# Valid agent capabilities (multi-select for plugins) +# Note: "Polling Required" is being migrated to "Ambient Agent" +agent_capabilities: + - Ambient Agent + - Structured Data Analyzer + +# Valid solution tags (business categorization, multi-select for plugins) +# Extracted from existing solution_tags and domain fields across all files +solution_tags: + - Access Management + - Approvals + - Customer Success + - Data Analysis + - Engineering + - Facilities + - Finance - Expense Management + - Finance - Other + - Finance - Payroll + - Finance - Procurement + - General + - HR - Benefits + - HR - Employee Records + - HR - Learning & Development + - HR - Onboarding + - HR - Other + - HR - Performance Management + - HR - Recruiting & Talent + - HR - Talent Management + - HR - Time & Absence + - HR - Workplace Culture + - IT + - Legal + - Manager + - Marketing + - Product + - Product Management + - Productivity + - Project Management + - Sales + - Support + - Ticketing + - Troubleshoot + - Workday diff --git a/ci/config_loader.py b/ci/config_loader.py new file mode 100644 index 00000000..df4328e1 --- /dev/null +++ b/ci/config_loader.py @@ -0,0 +1,58 @@ +""" +Configuration loader for V3 schema validation. +Loads and caches config.yaml to provide enum value lists. +""" + +import yaml +import os +from typing import List, Dict, Any + +# Path to config file relative to this module +CONFIG_FILE = os.path.join(os.path.dirname(__file__), "config.yaml") + +# Cache the loaded config +_config_cache = None + + +def load_config() -> Dict[str, Any]: + """Load and cache config.yaml""" + global _config_cache + if _config_cache is None: + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + _config_cache = yaml.safe_load(f) + return _config_cache + + +def get_valid_values(field: str) -> List[str]: + """ + Get valid enum values for a config field. + + Args: + field: Field name (e.g., 'availability', 'agent_capabilities', 'solution_tags') + + Returns: + List of valid values, or empty list if field not in config + """ + config = load_config() + return config.get(field, []) + + +def is_valid_value(field: str, value: str) -> bool: + """ + Check if a value is valid for a given field. + + Args: + field: Field name + value: Value to check + + Returns: + True if value is in the valid list, False otherwise + """ + valid_values = get_valid_values(field) + return value in valid_values + + +def get_config_version() -> int: + """Get the schema version from config""" + config = load_config() + return config.get('version', 3) diff --git a/ci/model.py b/ci/model.py index 46d888a5..2aa30292 100644 --- a/ci/model.py +++ b/ci/model.py @@ -1,5 +1,7 @@ from enum import Enum + +# V2 Schema Enums (kept for backwards compatibility during migration) class Fidelity(Enum): IDEA = "Idea" VALIDATED = "Validated" @@ -20,14 +22,80 @@ class DifficultyLevel(Enum): ADVANCED = "Advanced" -DIRECTORY_MAP = {ContentTypes.CONNECTOR: "connectors", ContentTypes.PLUGIN: "plugins"} +class Availability(Enum): + IDEA = "Idea" + VALIDATED = "Validated" + BUILT_IN = "Built-In" + INSTALLABLE = "Installable" + + +# Common constants +DIRECTORY_MAP = { + ContentTypes.CONNECTOR: "connectors", + ContentTypes.PLUGIN: "plugins" +} MARKDOWN_EXTENSION = '.md' README_FILENAME = f"README{MARKDOWN_EXTENSION}" LOGO_FILE = "logo.png" -class Availability(Enum): - IDEA = "Idea" - VALIDATED = "Validated" - BUILT_IN = "Built-In" - INSTALLABLE = "Installable" \ No newline at end of file + +# ============================================================================ +# V3 Schema Constants +# ============================================================================ + +# V3 Schema Version +V3_SCHEMA_VERSION = 3 + +# Path to config file +CONFIG_FILE = "ci/config.yaml" + +# Regex patterns for V3 validation +KEBAB_CASE_PATTERN = r'^[a-z0-9]+(?:-[a-z0-9]+)*$' +# Simplified Title Case pattern - actual validation more complex +TITLE_CASE_PATTERN = r'^[A-Z][a-z]*(?:\s+[A-Z][a-z]*)*$' + +# Description validation constants +PLUGIN_DESCRIPTION_PREFIX = "A plugin that" +CONNECTOR_DESCRIPTION_PREFIX = "A connector for" +DESCRIPTION_SUFFIX = "." + +# Purple Chat Link validation +PURPLE_CHAT_URL_PREFIX = ( + "https://marketplace.moveworks.com/purple-chat?conversation=" +) + +# V3 Required Fields by Content Type +CONNECTOR_REQUIRED_FIELDS = { + "name", # Title Case name + "availability", # Enum from config.yaml + "logo", # Logo URL (required) +} + +PLUGIN_REQUIRED_FIELDS = { + "name", # Title Case name + "description", # Must start with prefix and end with period + "availability", # Enum from config.yaml + "systems", # List of connector slugs (must exist as directories) + "purple_chat_link", # Must be valid URL + "solution_tags", # List from config.yaml +} + +# V3 Optional Fields by Content Type +CONNECTOR_OPTIONAL_FIELDS = { + "description", # Should end with period if present + "video", # Video URL + "redirects", # List of old slugs +} + +PLUGIN_OPTIONAL_FIELDS = { + "agent_capabilities", # List from config.yaml + "installation_asset_uuid", # UUID for installable plugins + "video", # Video URL + "redirects", # List of old slugs +} + +# All valid V3 fields (for strict schema validation) +# Any fields NOT in these sets will cause validation to FAIL +ALL_CONNECTOR_FIELDS = CONNECTOR_REQUIRED_FIELDS | CONNECTOR_OPTIONAL_FIELDS +ALL_PLUGIN_FIELDS = PLUGIN_REQUIRED_FIELDS | PLUGIN_OPTIONAL_FIELDS