- Add project creation modal to TeamDashboardPage with team_id assignment - Update createProject API function to accept optional team_id - Update seed data to create a "Demo Team" and assign all projects to it - Admin user is added as team owner when present
319 lines
10 KiB
Python
319 lines
10 KiB
Python
"""
|
|
Test data seeding for development environment.
|
|
"""
|
|
import hashlib
|
|
import logging
|
|
from sqlalchemy.orm import Session
|
|
|
|
from .models import Project, Package, Artifact, Tag, Upload, PackageVersion, ArtifactDependency, Team, TeamMembership, User
|
|
from .storage import get_storage
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Test data definitions
|
|
TEST_PROJECTS = [
|
|
{
|
|
"name": "frontend-libs",
|
|
"description": "Shared frontend libraries and components",
|
|
"is_public": True,
|
|
"packages": [
|
|
{
|
|
"name": "ui-components",
|
|
"description": "Reusable UI component library",
|
|
},
|
|
{
|
|
"name": "design-tokens",
|
|
"description": "Design system tokens and variables",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"name": "backend-services",
|
|
"description": "Backend microservices and shared utilities",
|
|
"is_public": True,
|
|
"packages": [
|
|
{
|
|
"name": "auth-lib",
|
|
"description": "Authentication and authorization library",
|
|
},
|
|
{
|
|
"name": "common-utils",
|
|
"description": "Common utility functions",
|
|
},
|
|
{
|
|
"name": "api-client",
|
|
"description": "Generated API client library",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"name": "mobile-apps",
|
|
"description": "Mobile application builds and assets",
|
|
"is_public": True,
|
|
"packages": [
|
|
{
|
|
"name": "ios-release",
|
|
"description": "iOS release builds",
|
|
},
|
|
{
|
|
"name": "android-release",
|
|
"description": "Android release builds",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"name": "internal-tools",
|
|
"description": "Internal development tools (private)",
|
|
"is_public": False,
|
|
"packages": [
|
|
{
|
|
"name": "dev-scripts",
|
|
"description": "Development automation scripts",
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
# Sample artifacts to create (content, tags, version)
|
|
TEST_ARTIFACTS = [
|
|
{
|
|
"project": "frontend-libs",
|
|
"package": "ui-components",
|
|
"content": b"/* UI Components v1.0.0 */\nexport const Button = () => {};\nexport const Input = () => {};\n",
|
|
"filename": "ui-components-1.0.0.js",
|
|
"content_type": "application/javascript",
|
|
"tags": ["v1.0.0", "latest"],
|
|
"version": "1.0.0",
|
|
},
|
|
{
|
|
"project": "frontend-libs",
|
|
"package": "ui-components",
|
|
"content": b"/* UI Components v1.1.0 */\nexport const Button = () => {};\nexport const Input = () => {};\nexport const Modal = () => {};\n",
|
|
"filename": "ui-components-1.1.0.js",
|
|
"content_type": "application/javascript",
|
|
"tags": ["v1.1.0"],
|
|
"version": "1.1.0",
|
|
},
|
|
{
|
|
"project": "frontend-libs",
|
|
"package": "design-tokens",
|
|
"content": b'{"colors": {"primary": "#007bff", "secondary": "#6c757d"}, "spacing": {"sm": "8px", "md": "16px"}}',
|
|
"filename": "tokens.json",
|
|
"content_type": "application/json",
|
|
"tags": ["v1.0.0", "latest"],
|
|
"version": "1.0.0",
|
|
},
|
|
{
|
|
"project": "backend-services",
|
|
"package": "common-utils",
|
|
"content": b"# Common Utils\n\ndef format_date(dt):\n return dt.isoformat()\n\ndef slugify(text):\n return text.lower().replace(' ', '-')\n",
|
|
"filename": "utils-2.0.0.py",
|
|
"content_type": "text/x-python",
|
|
"tags": ["v2.0.0", "stable", "latest"],
|
|
"version": "2.0.0",
|
|
},
|
|
{
|
|
"project": "backend-services",
|
|
"package": "auth-lib",
|
|
"content": b"package auth\n\nfunc ValidateToken(token string) bool {\n return len(token) > 0\n}\n",
|
|
"filename": "auth-lib-1.0.0.go",
|
|
"content_type": "text/x-go",
|
|
"tags": ["v1.0.0", "latest"],
|
|
"version": "1.0.0",
|
|
},
|
|
]
|
|
|
|
# Dependencies to create (source artifact -> dependency)
|
|
# Format: (source_project, source_package, source_version, dep_project, dep_package, version_constraint, tag_constraint)
|
|
TEST_DEPENDENCIES = [
|
|
# ui-components v1.1.0 depends on design-tokens v1.0.0
|
|
("frontend-libs", "ui-components", "1.1.0", "frontend-libs", "design-tokens", "1.0.0", None),
|
|
# auth-lib v1.0.0 depends on common-utils v2.0.0
|
|
("backend-services", "auth-lib", "1.0.0", "backend-services", "common-utils", "2.0.0", None),
|
|
# auth-lib v1.0.0 also depends on design-tokens (stable tag)
|
|
("backend-services", "auth-lib", "1.0.0", "frontend-libs", "design-tokens", None, "latest"),
|
|
]
|
|
|
|
|
|
def is_database_empty(db: Session) -> bool:
|
|
"""Check if the database has any projects."""
|
|
return db.query(Project).first() is None
|
|
|
|
|
|
def seed_database(db: Session) -> None:
|
|
"""Seed the database with test data."""
|
|
if not is_database_empty(db):
|
|
logger.info("Database already has data, skipping seed")
|
|
return
|
|
|
|
logger.info("Seeding database with test data...")
|
|
storage = get_storage()
|
|
|
|
# Find or use admin user for team ownership
|
|
admin_user = db.query(User).filter(User.username == "admin").first()
|
|
team_owner_username = admin_user.username if admin_user else "seed-user"
|
|
|
|
# Create a demo team
|
|
demo_team = Team(
|
|
name="Demo Team",
|
|
slug="demo-team",
|
|
description="A demonstration team with sample projects",
|
|
created_by=team_owner_username,
|
|
)
|
|
db.add(demo_team)
|
|
db.flush()
|
|
|
|
# Add admin user as team owner if they exist
|
|
if admin_user:
|
|
membership = TeamMembership(
|
|
team_id=demo_team.id,
|
|
user_id=admin_user.id,
|
|
role="owner",
|
|
invited_by=team_owner_username,
|
|
)
|
|
db.add(membership)
|
|
db.flush()
|
|
|
|
logger.info(f"Created team: {demo_team.name} ({demo_team.slug})")
|
|
|
|
# Create projects and packages
|
|
project_map = {}
|
|
package_map = {}
|
|
|
|
for project_data in TEST_PROJECTS:
|
|
project = Project(
|
|
name=project_data["name"],
|
|
description=project_data["description"],
|
|
is_public=project_data["is_public"],
|
|
created_by=team_owner_username,
|
|
team_id=demo_team.id, # Assign to demo team
|
|
)
|
|
db.add(project)
|
|
db.flush() # Get the ID
|
|
project_map[project_data["name"]] = project
|
|
|
|
for package_data in project_data["packages"]:
|
|
package = Package(
|
|
project_id=project.id,
|
|
name=package_data["name"],
|
|
description=package_data["description"],
|
|
)
|
|
db.add(package)
|
|
db.flush()
|
|
package_map[(project_data["name"], package_data["name"])] = package
|
|
|
|
logger.info(f"Created {len(project_map)} projects and {len(package_map)} packages (assigned to {demo_team.slug})")
|
|
|
|
# Create artifacts, tags, and versions
|
|
artifact_count = 0
|
|
tag_count = 0
|
|
version_count = 0
|
|
|
|
for artifact_data in TEST_ARTIFACTS:
|
|
project = project_map[artifact_data["project"]]
|
|
package = package_map[(artifact_data["project"], artifact_data["package"])]
|
|
|
|
content = artifact_data["content"]
|
|
sha256_hash = hashlib.sha256(content).hexdigest()
|
|
size = len(content)
|
|
s3_key = f"fruits/{sha256_hash[:2]}/{sha256_hash[2:4]}/{sha256_hash}"
|
|
|
|
# Store in S3
|
|
try:
|
|
storage.client.put_object(
|
|
Bucket=storage.bucket,
|
|
Key=s3_key,
|
|
Body=content,
|
|
)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to store artifact in S3: {e}")
|
|
continue
|
|
|
|
# Calculate ref_count: tags + version (if present)
|
|
ref_count = len(artifact_data["tags"])
|
|
if artifact_data.get("version"):
|
|
ref_count += 1
|
|
|
|
# Create artifact record
|
|
artifact = Artifact(
|
|
id=sha256_hash,
|
|
size=size,
|
|
content_type=artifact_data["content_type"],
|
|
original_name=artifact_data["filename"],
|
|
created_by=team_owner_username,
|
|
s3_key=s3_key,
|
|
ref_count=ref_count,
|
|
)
|
|
db.add(artifact)
|
|
|
|
# Create upload record
|
|
upload = Upload(
|
|
artifact_id=sha256_hash,
|
|
package_id=package.id,
|
|
original_name=artifact_data["filename"],
|
|
uploaded_by="seed-user",
|
|
)
|
|
db.add(upload)
|
|
artifact_count += 1
|
|
|
|
# Create version record if specified
|
|
if artifact_data.get("version"):
|
|
version = PackageVersion(
|
|
package_id=package.id,
|
|
artifact_id=sha256_hash,
|
|
version=artifact_data["version"],
|
|
version_source="explicit",
|
|
created_by=team_owner_username,
|
|
)
|
|
db.add(version)
|
|
version_count += 1
|
|
|
|
# Create tags
|
|
for tag_name in artifact_data["tags"]:
|
|
tag = Tag(
|
|
package_id=package.id,
|
|
name=tag_name,
|
|
artifact_id=sha256_hash,
|
|
created_by=team_owner_username,
|
|
)
|
|
db.add(tag)
|
|
tag_count += 1
|
|
|
|
db.flush()
|
|
|
|
# Create dependencies
|
|
dependency_count = 0
|
|
for dep_data in TEST_DEPENDENCIES:
|
|
src_project, src_package, src_version, dep_project, dep_package, version_constraint, tag_constraint = dep_data
|
|
|
|
# Find the source artifact by looking up its version
|
|
src_pkg = package_map.get((src_project, src_package))
|
|
if not src_pkg:
|
|
logger.warning(f"Source package not found: {src_project}/{src_package}")
|
|
continue
|
|
|
|
# Find the artifact for this version
|
|
src_version_record = db.query(PackageVersion).filter(
|
|
PackageVersion.package_id == src_pkg.id,
|
|
PackageVersion.version == src_version,
|
|
).first()
|
|
|
|
if not src_version_record:
|
|
logger.warning(f"Source version not found: {src_project}/{src_package}@{src_version}")
|
|
continue
|
|
|
|
# Create the dependency
|
|
dependency = ArtifactDependency(
|
|
artifact_id=src_version_record.artifact_id,
|
|
dependency_project=dep_project,
|
|
dependency_package=dep_package,
|
|
version_constraint=version_constraint,
|
|
tag_constraint=tag_constraint,
|
|
)
|
|
db.add(dependency)
|
|
dependency_count += 1
|
|
|
|
db.commit()
|
|
logger.info(f"Created {artifact_count} artifacts, {tag_count} tags, {version_count} versions, and {dependency_count} dependencies")
|
|
logger.info("Database seeding complete")
|