artifacts!
This commit is contained in:
@@ -186,6 +186,18 @@ END $$;
|
||||
|
||||
COMMENT ON TYPE artifact_retention_enum IS 'Type of retention policy';
|
||||
|
||||
-- ArtifactVisibility enum
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE artifact_visibility_enum AS ENUM (
|
||||
'public',
|
||||
'private'
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
COMMENT ON TYPE artifact_visibility_enum IS 'Visibility of an artifact (public = viewable by all users, private = scoped by owner)';
|
||||
|
||||
|
||||
-- PackEnvironmentStatus enum
|
||||
DO $$ BEGIN
|
||||
|
||||
@@ -143,6 +143,7 @@ CREATE TABLE artifact (
|
||||
scope owner_type_enum NOT NULL DEFAULT 'system',
|
||||
owner TEXT NOT NULL DEFAULT '',
|
||||
type artifact_type_enum NOT NULL,
|
||||
visibility artifact_visibility_enum NOT NULL DEFAULT 'private',
|
||||
retention_policy artifact_retention_enum NOT NULL DEFAULT 'versions',
|
||||
retention_limit INTEGER NOT NULL DEFAULT 1,
|
||||
created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
@@ -157,6 +158,8 @@ CREATE INDEX idx_artifact_type ON artifact(type);
|
||||
CREATE INDEX idx_artifact_created ON artifact(created DESC);
|
||||
CREATE INDEX idx_artifact_scope_owner ON artifact(scope, owner);
|
||||
CREATE INDEX idx_artifact_type_created ON artifact(type, created DESC);
|
||||
CREATE INDEX idx_artifact_visibility ON artifact(visibility);
|
||||
CREATE INDEX idx_artifact_visibility_scope ON artifact(visibility, scope, owner);
|
||||
|
||||
-- Trigger
|
||||
CREATE TRIGGER update_artifact_updated
|
||||
@@ -170,6 +173,7 @@ COMMENT ON COLUMN artifact.ref IS 'Artifact reference/path';
|
||||
COMMENT ON COLUMN artifact.scope IS 'Owner type (system, identity, pack, action, sensor)';
|
||||
COMMENT ON COLUMN artifact.owner IS 'Owner identifier';
|
||||
COMMENT ON COLUMN artifact.type IS 'Artifact type (file, url, progress, etc.)';
|
||||
COMMENT ON COLUMN artifact.visibility IS 'Visibility level: public (all users) or private (scoped by scope/owner)';
|
||||
COMMENT ON COLUMN artifact.retention_policy IS 'How to retain artifacts (versions, days, hours, minutes)';
|
||||
COMMENT ON COLUMN artifact.retention_limit IS 'Numeric limit for retention policy';
|
||||
|
||||
|
||||
@@ -329,3 +329,98 @@ CREATE TRIGGER workflow_execution_status_changed_notify
|
||||
EXECUTE FUNCTION notify_workflow_execution_status_changed();
|
||||
|
||||
COMMENT ON FUNCTION notify_workflow_execution_status_changed() IS 'Sends workflow execution status change notifications via PostgreSQL LISTEN/NOTIFY';
|
||||
|
||||
-- ============================================================================
|
||||
-- ARTIFACT NOTIFICATIONS
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to notify on artifact creation
|
||||
CREATE OR REPLACE FUNCTION notify_artifact_created()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
payload JSON;
|
||||
BEGIN
|
||||
payload := json_build_object(
|
||||
'entity_type', 'artifact',
|
||||
'entity_id', NEW.id,
|
||||
'id', NEW.id,
|
||||
'ref', NEW.ref,
|
||||
'type', NEW.type,
|
||||
'visibility', NEW.visibility,
|
||||
'name', NEW.name,
|
||||
'execution', NEW.execution,
|
||||
'scope', NEW.scope,
|
||||
'owner', NEW.owner,
|
||||
'content_type', NEW.content_type,
|
||||
'size_bytes', NEW.size_bytes,
|
||||
'created', NEW.created
|
||||
);
|
||||
|
||||
PERFORM pg_notify('artifact_created', payload::text);
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger on artifact table for creation
|
||||
CREATE TRIGGER artifact_created_notify
|
||||
AFTER INSERT ON artifact
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION notify_artifact_created();
|
||||
|
||||
COMMENT ON FUNCTION notify_artifact_created() IS 'Sends artifact creation notifications via PostgreSQL LISTEN/NOTIFY';
|
||||
|
||||
-- Function to notify on artifact updates (progress appends, data changes)
|
||||
CREATE OR REPLACE FUNCTION notify_artifact_updated()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
payload JSON;
|
||||
latest_percent DOUBLE PRECISION;
|
||||
latest_message TEXT;
|
||||
entry_count INTEGER;
|
||||
BEGIN
|
||||
-- Only notify on actual changes
|
||||
IF TG_OP = 'UPDATE' THEN
|
||||
-- Extract progress summary from data array if this is a progress artifact
|
||||
IF NEW.type = 'progress' AND NEW.data IS NOT NULL AND jsonb_typeof(NEW.data) = 'array' THEN
|
||||
entry_count := jsonb_array_length(NEW.data);
|
||||
IF entry_count > 0 THEN
|
||||
latest_percent := (NEW.data -> (entry_count - 1) ->> 'percent')::DOUBLE PRECISION;
|
||||
latest_message := NEW.data -> (entry_count - 1) ->> 'message';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
payload := json_build_object(
|
||||
'entity_type', 'artifact',
|
||||
'entity_id', NEW.id,
|
||||
'id', NEW.id,
|
||||
'ref', NEW.ref,
|
||||
'type', NEW.type,
|
||||
'visibility', NEW.visibility,
|
||||
'name', NEW.name,
|
||||
'execution', NEW.execution,
|
||||
'scope', NEW.scope,
|
||||
'owner', NEW.owner,
|
||||
'content_type', NEW.content_type,
|
||||
'size_bytes', NEW.size_bytes,
|
||||
'progress_percent', latest_percent,
|
||||
'progress_message', latest_message,
|
||||
'progress_entries', entry_count,
|
||||
'created', NEW.created,
|
||||
'updated', NEW.updated
|
||||
);
|
||||
|
||||
PERFORM pg_notify('artifact_updated', payload::text);
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger on artifact table for updates
|
||||
CREATE TRIGGER artifact_updated_notify
|
||||
AFTER UPDATE ON artifact
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION notify_artifact_updated();
|
||||
|
||||
COMMENT ON FUNCTION notify_artifact_updated() IS 'Sends artifact update notifications via PostgreSQL LISTEN/NOTIFY (includes progress summary for progress-type artifacts)';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- Migration: Artifact Content System
|
||||
-- Description: Enhances the artifact table with content fields (name, description,
|
||||
-- content_type, size_bytes, execution link, structured data) and creates
|
||||
-- the artifact_version table for versioned file/data storage.
|
||||
-- content_type, size_bytes, execution link, structured data, visibility)
|
||||
-- and creates the artifact_version table for versioned file/data storage.
|
||||
--
|
||||
-- The artifact table now serves as the "header" for a logical artifact,
|
||||
-- while artifact_version rows hold the actual immutable content snapshots.
|
||||
@@ -33,10 +33,19 @@ ALTER TABLE artifact ADD COLUMN IF NOT EXISTS execution BIGINT;
|
||||
-- Progress artifacts append entries here; file artifacts may store parsed metadata.
|
||||
ALTER TABLE artifact ADD COLUMN IF NOT EXISTS data JSONB;
|
||||
|
||||
-- Visibility: public artifacts are viewable by all authenticated users;
|
||||
-- private artifacts are restricted based on the artifact's scope/owner.
|
||||
-- The scope (identity, action, pack, etc.) + owner fields define who can access
|
||||
-- a private artifact. Full RBAC enforcement is deferred — for now the column
|
||||
-- enables filtering and is available for future permission checks.
|
||||
ALTER TABLE artifact ADD COLUMN IF NOT EXISTS visibility artifact_visibility_enum NOT NULL DEFAULT 'private';
|
||||
|
||||
-- New indexes for the added columns
|
||||
CREATE INDEX IF NOT EXISTS idx_artifact_execution ON artifact(execution);
|
||||
CREATE INDEX IF NOT EXISTS idx_artifact_name ON artifact(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_artifact_execution_type ON artifact(execution, type);
|
||||
CREATE INDEX IF NOT EXISTS idx_artifact_visibility ON artifact(visibility);
|
||||
CREATE INDEX IF NOT EXISTS idx_artifact_visibility_scope ON artifact(visibility, scope, owner);
|
||||
|
||||
-- Comments for new columns
|
||||
COMMENT ON COLUMN artifact.name IS 'Human-readable artifact name';
|
||||
@@ -45,6 +54,7 @@ COMMENT ON COLUMN artifact.content_type IS 'MIME content type (e.g. application/
|
||||
COMMENT ON COLUMN artifact.size_bytes IS 'Size of latest version content in bytes';
|
||||
COMMENT ON COLUMN artifact.execution IS 'Execution that produced this artifact (no FK — execution is a hypertable)';
|
||||
COMMENT ON COLUMN artifact.data IS 'Structured JSONB data for progress artifacts or metadata';
|
||||
COMMENT ON COLUMN artifact.visibility IS 'Access visibility: public (all users) or private (scope/owner-restricted)';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
@@ -69,13 +79,18 @@ CREATE TABLE artifact_version (
|
||||
-- Size of the content in bytes
|
||||
size_bytes BIGINT,
|
||||
|
||||
-- Binary content (file uploads). Use BYTEA for simplicity; large files
|
||||
-- should use external object storage in production (future enhancement).
|
||||
-- Binary content (file uploads, DB-stored). NULL for file-backed versions.
|
||||
content BYTEA,
|
||||
|
||||
-- Structured content (JSON payloads, parsed results, etc.)
|
||||
content_json JSONB,
|
||||
|
||||
-- Relative path from artifacts_dir root for disk-stored content.
|
||||
-- When set, content BYTEA is NULL — file lives on shared volume.
|
||||
-- Pattern: {ref_slug}/v{version}.{ext}
|
||||
-- e.g., "mypack/build_log/v1.txt"
|
||||
file_path TEXT,
|
||||
|
||||
-- Free-form metadata about this version (e.g. commit hash, build number)
|
||||
meta JSONB,
|
||||
|
||||
@@ -94,6 +109,7 @@ ALTER TABLE artifact_version
|
||||
CREATE INDEX idx_artifact_version_artifact ON artifact_version(artifact);
|
||||
CREATE INDEX idx_artifact_version_artifact_version ON artifact_version(artifact, version DESC);
|
||||
CREATE INDEX idx_artifact_version_created ON artifact_version(created DESC);
|
||||
CREATE INDEX idx_artifact_version_file_path ON artifact_version(file_path) WHERE file_path IS NOT NULL;
|
||||
|
||||
-- Comments
|
||||
COMMENT ON TABLE artifact_version IS 'Immutable content snapshots for artifacts (file uploads, structured data)';
|
||||
@@ -105,6 +121,7 @@ COMMENT ON COLUMN artifact_version.content IS 'Binary content (file data)';
|
||||
COMMENT ON COLUMN artifact_version.content_json IS 'Structured JSON content';
|
||||
COMMENT ON COLUMN artifact_version.meta IS 'Free-form metadata about this version';
|
||||
COMMENT ON COLUMN artifact_version.created_by IS 'Who created this version (identity ref, action ref, system)';
|
||||
COMMENT ON COLUMN artifact_version.file_path IS 'Relative path from artifacts_dir root for disk-stored content. When set, content BYTEA is NULL — file lives on shared volume.';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user