simplecontent

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2025 License: MIT Imports: 6 Imported by: 3

README

Simple Content Library

A reusable Go library for content management with pluggable storage backends and repository implementations.

Overview

The simplecontent package provides a clean, pluggable architecture for content management systems. It separates concerns between:

  • Domain types: Content, Object, metadata types
  • Interfaces: Service, Repository, BlobStore, EventSink, Previewer
  • Implementations: Memory, PostgreSQL, S3, filesystem storage backends

Quick Start

package main

import (
    "context"
    "log"
    
    "github.com/tendant/simple-content/pkg/simplecontent"
    "github.com/tendant/simple-content/pkg/simplecontent/repo/memory"
    "github.com/tendant/simple-content/pkg/simplecontent/storage/memory"
)

func main() {
    // Create repository and storage backends
    repo := memory.New()
    store := memorystorage.New()
    
    // Create service with functional options
    svc, err := simplecontent.New(
        simplecontent.WithRepository(repo),
        simplecontent.WithBlobStore("memory", store),
    )
    if err != nil {
        log.Fatal(err)
    }
    
    ctx := context.Background()
    
    // Create content
    content, err := svc.CreateContent(ctx, simplecontent.CreateContentRequest{
        OwnerID:     uuid.New(),
        TenantID:    uuid.New(), 
        Name:        "My Document",
        Description: "A sample document",
    })
    if err != nil {
        log.Fatal(err)
    }
    
    // Create object for storage
    object, err := svc.CreateObject(ctx, simplecontent.CreateObjectRequest{
        ContentID:          content.ID,
        StorageBackendName: "memory",
        Version:            1,
    })
    if err != nil {
        log.Fatal(err)
    }
    
    // Upload data
    data := strings.NewReader("Hello, World!")
    err = svc.UploadObject(ctx, object.ID, data)
    if err != nil {
        log.Fatal(err)
    }
    
    // Download data  
    reader, err := svc.DownloadObject(ctx, object.ID)
    if err != nil {
        log.Fatal(err)
    }
    defer reader.Close()
    
    // Read downloaded content
    content, err := io.ReadAll(reader)
    fmt.Printf("Downloaded: %s\\n", content)
}

Architecture

Core Interfaces
  • Service: Main interface providing all content management operations
  • Repository: Data persistence abstraction for contents, objects, and metadata
  • BlobStore: Storage backend abstraction for binary data
  • EventSink: Event handling for lifecycle events
  • Previewer: Content preview generation
Available Implementations
Repositories
  • repo/memory: In-memory repository (testing)
  • repo/postgres: PostgreSQL repository (production)
Storage Backends
  • storage/memory: In-memory storage (testing)
  • storage/fs: Filesystem storage
  • storage/s3: S3-compatible storage
Configuration Options

The service supports functional options for configuration:

svc, err := simplecontent.New(
    simplecontent.WithRepository(postgresRepo),
    simplecontent.WithBlobStore("s3-primary", s3Store),
    simplecontent.WithBlobStore("s3-backup", s3BackupStore),
    simplecontent.WithBlobStore("local", fsStore),
    simplecontent.WithEventSink(eventSink),
    simplecontent.WithPreviewer(previewer),
)

Features

  • Pluggable architecture: Swap repositories and storage backends easily
  • Multi-tenant: Built-in tenant isolation
  • Versioning: Support for content versions
  • Metadata management: Rich metadata support for content and objects
  • Event system: Lifecycle event notifications
  • Preview generation: Extensible preview system
  • Error handling: Typed errors for better error handling

Use Cases

  • Document management systems
  • Media asset management
  • File storage services
  • Content delivery platforms
  • Multi-tenant SaaS applications

Testing

The library includes in-memory implementations perfect for testing:

func TestMyFeature(t *testing.T) {
    repo := memory.New()
    store := memorystorage.New()
    
    svc, _ := simplecontent.New(
        simplecontent.WithRepository(repo),
        simplecontent.WithBlobStore("test", store),
    )
    
    // Test your code...
}

Documentation

Index

Constants

View Source
const (
	ContentStatusCreated  = "created"
	ContentStatusUploaded = "uploaded"
)

Content status constants

View Source
const (
	ContentDerivationTypeOriginal = "original"
	ContentDerivationTypeDerived  = "derived"
)

Content derivation type constants

View Source
const (
	ContentDerivedTHUMBNAIL720 = "THUMBNAIL_720"
	ContentDerivedTHUMBNAIL480 = "THUMBNAIL_480"
	ContentDerivedTHUMBNAIL256 = "THUMBNAIL_256"
	ContentDerivedTHUMBNAIL128 = "THUMBNAIL_128"
	ContentDerivedConversion   = "CONVERSION"
)

Content derived derivation type constants

View Source
const (
	ObjectStatusCreated    = "created"
	ObjectStatusUploading  = "uploading"
	ObjectStatusUploaded   = "uploaded"
	ObjectStatusProcessing = "processing"
	ObjectStatusProcessed  = "processed"
	ObjectStatusFailed     = "failed"
	ObjectStatusDeleted    = "deleted"
)

Object status constants

Variables

View Source
var (
	// ErrContentNotFound indicates a content was not found
	ErrContentNotFound = errors.New("content not found")

	// ErrObjectNotFound indicates an object was not found
	ErrObjectNotFound = errors.New("object not found")

	// ErrStorageBackendNotFound indicates a storage backend was not found
	ErrStorageBackendNotFound = errors.New("storage backend not found")

	// ErrInvalidContentStatus indicates an invalid content status
	ErrInvalidContentStatus = errors.New("invalid content status")

	// ErrInvalidObjectStatus indicates an invalid object status
	ErrInvalidObjectStatus = errors.New("invalid object status")

	// ErrUploadFailed indicates an upload operation failed
	ErrUploadFailed = errors.New("upload failed")

	// ErrDownloadFailed indicates a download operation failed
	ErrDownloadFailed = errors.New("download failed")
)

Error types

Functions

This section is empty.

Types

type BasicImagePreviewer

type BasicImagePreviewer struct {
	// contains filtered or unexported fields
}

BasicImagePreviewer is a simple previewer that generates preview URLs for common image types

func (*BasicImagePreviewer) GeneratePreview

func (b *BasicImagePreviewer) GeneratePreview(ctx context.Context, object *Object, blobStore BlobStore) (*ObjectPreview, error)

GeneratePreview generates a preview for supported image types

func (*BasicImagePreviewer) SupportsContent

func (b *BasicImagePreviewer) SupportsContent(mimeType string) bool

SupportsContent returns true if the MIME type is a supported image type

type BlobStore

type BlobStore interface {
	// GetUploadURL returns a URL for uploading content
	GetUploadURL(ctx context.Context, objectKey string) (string, error)

	// Upload uploads content directly
	Upload(ctx context.Context, objectKey string, reader io.Reader) error

	// UploadWithParams uploads content with additional parameters
	UploadWithParams(ctx context.Context, reader io.Reader, params UploadParams) error

	// GetDownloadURL returns a URL for downloading content
	GetDownloadURL(ctx context.Context, objectKey string, downloadFilename string) (string, error)

	// GetPreviewURL returns a URL for previewing content
	GetPreviewURL(ctx context.Context, objectKey string) (string, error)

	// Download downloads content directly
	Download(ctx context.Context, objectKey string) (io.ReadCloser, error)

	// Delete deletes content
	Delete(ctx context.Context, objectKey string) error

	// GetObjectMeta retrieves metadata for an object
	GetObjectMeta(ctx context.Context, objectKey string) (*ObjectMeta, error)
}

BlobStore defines the interface for storage backends

type Content

type Content struct {
	ID             uuid.UUID `json:"id"`
	TenantID       uuid.UUID `json:"tenant_id"`
	OwnerID        uuid.UUID `json:"owner_id"`
	OwnerType      string    `json:"owner_type,omitempty"`
	Name           string    `json:"name,omitempty"`
	Description    string    `json:"description,omitempty"`
	DocumentType   string    `json:"document_type,omitempty"`
	Status         string    `json:"status"`
	DerivationType string    `json:"derivation_type,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

Content represents a logical content entity

type ContentError

type ContentError struct {
	ContentID uuid.UUID
	Op        string
	Err       error
}

ContentError represents an error related to content operations

func (*ContentError) Error

func (e *ContentError) Error() string

func (*ContentError) Unwrap

func (e *ContentError) Unwrap() error

type ContentMetadata

type ContentMetadata struct {
	ContentID         uuid.UUID              `json:"content_id"`
	Tags              []string               `json:"tags,omitempty"`
	FileSize          int64                  `json:"file_size,omitempty"`
	FileName          string                 `json:"file_name,omitempty"`
	MimeType          string                 `json:"mime_type"`
	Checksum          string                 `json:"checksum,omitempty"`
	ChecksumAlgorithm string                 `json:"checksum_algorithm,omitempty"`
	Metadata          map[string]interface{} `json:"metadata"`
	CreatedAt         time.Time              `json:"created_at"`
	UpdatedAt         time.Time              `json:"updated_at"`
}

ContentMetadata represents metadata for a content

type CreateContentRequest

type CreateContentRequest struct {
	OwnerID        uuid.UUID
	TenantID       uuid.UUID
	Name           string
	Description    string
	DocumentType   string
	DerivationType string
}

CreateContentRequest contains parameters for creating new content

type CreateDerivedContentParams

type CreateDerivedContentParams struct {
	ParentID           uuid.UUID
	DerivedContentID   uuid.UUID
	DerivationType     string
	DerivationParams   map[string]interface{}
	ProcessingMetadata map[string]interface{}
}

CreateDerivedContentParams contains parameters for creating derived content relationships

type CreateDerivedContentRequest

type CreateDerivedContentRequest struct {
	ParentID       uuid.UUID
	OwnerID        uuid.UUID
	TenantID       uuid.UUID
	Category       string
	DerivationType string
	Metadata       map[string]interface{}
}

CreateDerivedContentRequest contains parameters for creating derived content

type CreateObjectRequest

type CreateObjectRequest struct {
	ContentID          uuid.UUID
	StorageBackendName string
	Version            int
	ObjectKey          string
}

CreateObjectRequest contains parameters for creating an object

type DerivedContent

type DerivedContent struct {
	ParentID           uuid.UUID              `json:"parent_id"`
	ContentID          uuid.UUID              `json:"content_id"`
	DerivationType     string                 `json:"derivation_type"`
	DerivationParams   map[string]interface{} `json:"derivation_params"`
	ProcessingMetadata map[string]interface{} `json:"processing_metadata"`
	CreatedAt          time.Time              `json:"created_at"`
	UpdatedAt          time.Time              `json:"updated_at"`
	DocumentType       string                 `json:"document_type"`
	Status             string                 `json:"status"`
}

DerivedContent represents content derived from a parent content

type EventSink

type EventSink interface {
	// ContentCreated is fired when content is created
	ContentCreated(ctx context.Context, content *Content) error

	// ContentUpdated is fired when content is updated
	ContentUpdated(ctx context.Context, content *Content) error

	// ContentDeleted is fired when content is deleted
	ContentDeleted(ctx context.Context, contentID uuid.UUID) error

	// ObjectCreated is fired when an object is created
	ObjectCreated(ctx context.Context, object *Object) error

	// ObjectUploaded is fired when an object is uploaded
	ObjectUploaded(ctx context.Context, object *Object) error

	// ObjectDeleted is fired when an object is deleted
	ObjectDeleted(ctx context.Context, objectID uuid.UUID) error
}

EventSink defines the interface for event handling

func NewLoggingEventSink

func NewLoggingEventSink(logger Logger) EventSink

NewLoggingEventSink creates a new logging event sink

func NewNoopEventSink

func NewNoopEventSink() EventSink

NewNoopEventSink creates a new no-operation event sink

type ListContentRequest

type ListContentRequest struct {
	OwnerID  uuid.UUID
	TenantID uuid.UUID
}

ListContentRequest contains parameters for listing content

type ListDerivedContentParams

type ListDerivedContentParams struct {
	ParentID       *uuid.UUID
	DerivationType *string
	Limit          *int
	Offset         *int
}

ListDerivedContentParams contains parameters for listing derived content

type Logger

type Logger interface {
	Infof(format string, args ...interface{})
	Errorf(format string, args ...interface{})
}

Logger interface for logging events

type LoggingEventSink

type LoggingEventSink struct {
	// contains filtered or unexported fields
}

LoggingEventSink is an event sink that logs events but takes no other action Useful for development and debugging

func (*LoggingEventSink) ContentCreated

func (l *LoggingEventSink) ContentCreated(ctx context.Context, content *Content) error

ContentCreated logs the content creation event

func (*LoggingEventSink) ContentDeleted

func (l *LoggingEventSink) ContentDeleted(ctx context.Context, contentID uuid.UUID) error

ContentDeleted logs the content deletion event

func (*LoggingEventSink) ContentUpdated

func (l *LoggingEventSink) ContentUpdated(ctx context.Context, content *Content) error

ContentUpdated logs the content update event

func (*LoggingEventSink) ObjectCreated

func (l *LoggingEventSink) ObjectCreated(ctx context.Context, object *Object) error

ObjectCreated logs the object creation event

func (*LoggingEventSink) ObjectDeleted

func (l *LoggingEventSink) ObjectDeleted(ctx context.Context, objectID uuid.UUID) error

ObjectDeleted logs the object deletion event

func (*LoggingEventSink) ObjectUploaded

func (l *LoggingEventSink) ObjectUploaded(ctx context.Context, object *Object) error

ObjectUploaded logs the object upload event

type NoopEventSink

type NoopEventSink struct{}

NoopEventSink is a no-operation implementation of EventSink Useful for production when you don't need event handling or for testing

func (*NoopEventSink) ContentCreated

func (n *NoopEventSink) ContentCreated(ctx context.Context, content *Content) error

ContentCreated does nothing and returns nil

func (*NoopEventSink) ContentDeleted

func (n *NoopEventSink) ContentDeleted(ctx context.Context, contentID uuid.UUID) error

ContentDeleted does nothing and returns nil

func (*NoopEventSink) ContentUpdated

func (n *NoopEventSink) ContentUpdated(ctx context.Context, content *Content) error

ContentUpdated does nothing and returns nil

func (*NoopEventSink) ObjectCreated

func (n *NoopEventSink) ObjectCreated(ctx context.Context, object *Object) error

ObjectCreated does nothing and returns nil

func (*NoopEventSink) ObjectDeleted

func (n *NoopEventSink) ObjectDeleted(ctx context.Context, objectID uuid.UUID) error

ObjectDeleted does nothing and returns nil

func (*NoopEventSink) ObjectUploaded

func (n *NoopEventSink) ObjectUploaded(ctx context.Context, object *Object) error

ObjectUploaded does nothing and returns nil

type NoopPreviewer

type NoopPreviewer struct{}

NoopPreviewer is a no-operation implementation of Previewer Always returns nil (no preview generated) and supports no content types

func (*NoopPreviewer) GeneratePreview

func (n *NoopPreviewer) GeneratePreview(ctx context.Context, object *Object, blobStore BlobStore) (*ObjectPreview, error)

GeneratePreview always returns nil (no preview generated)

func (*NoopPreviewer) SupportsContent

func (n *NoopPreviewer) SupportsContent(mimeType string) bool

SupportsContent always returns false (supports no content types)

type Object

type Object struct {
	ID                 uuid.UUID `json:"id"`
	ContentID          uuid.UUID `json:"content_id"`
	StorageBackendName string    `json:"storage_backend_name"`
	StorageClass       string    `json:"storage_class,omitempty"`
	ObjectKey          string    `json:"object_key"`
	FileName           string    `json:"file_name,omitempty"`
	Version            int       `json:"version"`
	ObjectType         string    `json:"object_type,omitempty"`
	Status             string    `json:"status"`
	CreatedAt          time.Time `json:"created_at"`
	UpdatedAt          time.Time `json:"updated_at"`
}

Object represents a physical object stored in a storage backend

type ObjectError

type ObjectError struct {
	ObjectID uuid.UUID
	Op       string
	Err      error
}

ObjectError represents an error related to object operations

func (*ObjectError) Error

func (e *ObjectError) Error() string

func (*ObjectError) Unwrap

func (e *ObjectError) Unwrap() error

type ObjectMeta

type ObjectMeta struct {
	Key         string
	Size        int64
	ContentType string
	UpdatedAt   time.Time
	ETag        string
	Metadata    map[string]string
}

ObjectMeta contains metadata about an object in storage

type ObjectMetadata

type ObjectMetadata struct {
	ObjectID  uuid.UUID              `json:"object_id"`
	SizeBytes int64                  `json:"size_bytes"`
	MimeType  string                 `json:"mime_type"`
	ETag      string                 `json:"etag,omitempty"`
	Metadata  map[string]interface{} `json:"metadata"`
	CreatedAt time.Time              `json:"created_at"`
	UpdatedAt time.Time              `json:"updated_at"`
}

ObjectMetadata represents metadata about an object

type ObjectPreview

type ObjectPreview struct {
	ID          uuid.UUID `json:"id"`
	ObjectID    uuid.UUID `json:"object_id"`
	PreviewType string    `json:"preview_type"`
	Status      string    `json:"status"`
	PreviewURL  string    `json:"preview_url"`
	CreatedAt   time.Time `json:"created_at"`
}

ObjectPreview represents a preview generated from an object

type Option

type Option func(*service)

Option represents a functional option for configuring the service

func WithBlobStore

func WithBlobStore(name string, store BlobStore) Option

WithBlobStore adds a blob storage backend

func WithEventSink

func WithEventSink(sink EventSink) Option

WithEventSink sets the event sink for the service

func WithPreviewer

func WithPreviewer(previewer Previewer) Option

WithPreviewer sets the previewer for the service

func WithRepository

func WithRepository(repo Repository) Option

WithRepository sets the repository for the service

type Previewer

type Previewer interface {
	// GeneratePreview generates a preview for the given object
	GeneratePreview(ctx context.Context, object *Object, blobStore BlobStore) (*ObjectPreview, error)

	// SupportsContent returns true if the previewer supports the given content type
	SupportsContent(mimeType string) bool
}

Previewer defines the interface for content preview generation

func NewBasicImagePreviewer

func NewBasicImagePreviewer() Previewer

NewBasicImagePreviewer creates a new basic image previewer

func NewNoopPreviewer

func NewNoopPreviewer() Previewer

NewNoopPreviewer creates a new no-operation previewer

type Repository

type Repository interface {
	// Content operations
	CreateContent(ctx context.Context, content *Content) error
	GetContent(ctx context.Context, id uuid.UUID) (*Content, error)
	UpdateContent(ctx context.Context, content *Content) error
	DeleteContent(ctx context.Context, id uuid.UUID) error
	ListContent(ctx context.Context, ownerID, tenantID uuid.UUID) ([]*Content, error)

	// Content metadata operations
	SetContentMetadata(ctx context.Context, metadata *ContentMetadata) error
	GetContentMetadata(ctx context.Context, contentID uuid.UUID) (*ContentMetadata, error)

	// Derived content operations
	CreateDerivedContentRelationship(ctx context.Context, params CreateDerivedContentParams) (*DerivedContent, error)
	ListDerivedContent(ctx context.Context, params ListDerivedContentParams) ([]*DerivedContent, error)

	// Object operations
	CreateObject(ctx context.Context, object *Object) error
	GetObject(ctx context.Context, id uuid.UUID) (*Object, error)
	GetObjectsByContentID(ctx context.Context, contentID uuid.UUID) ([]*Object, error)
	GetObjectByObjectKeyAndStorageBackendName(ctx context.Context, objectKey, storageBackendName string) (*Object, error)
	UpdateObject(ctx context.Context, object *Object) error
	DeleteObject(ctx context.Context, id uuid.UUID) error

	// Object metadata operations
	SetObjectMetadata(ctx context.Context, metadata *ObjectMetadata) error
	GetObjectMetadata(ctx context.Context, objectID uuid.UUID) (*ObjectMetadata, error)
}

Repository defines the interface for content and object persistence

type Service

type Service interface {
	// Content operations
	CreateContent(ctx context.Context, req CreateContentRequest) (*Content, error)
	CreateDerivedContent(ctx context.Context, req CreateDerivedContentRequest) (*Content, error)
	GetContent(ctx context.Context, id uuid.UUID) (*Content, error)
	UpdateContent(ctx context.Context, req UpdateContentRequest) error
	DeleteContent(ctx context.Context, id uuid.UUID) error
	ListContent(ctx context.Context, req ListContentRequest) ([]*Content, error)

	// Content metadata operations
	SetContentMetadata(ctx context.Context, req SetContentMetadataRequest) error
	GetContentMetadata(ctx context.Context, contentID uuid.UUID) (*ContentMetadata, error)

	// Object operations
	CreateObject(ctx context.Context, req CreateObjectRequest) (*Object, error)
	GetObject(ctx context.Context, id uuid.UUID) (*Object, error)
	GetObjectsByContentID(ctx context.Context, contentID uuid.UUID) ([]*Object, error)
	UpdateObject(ctx context.Context, object *Object) error
	DeleteObject(ctx context.Context, id uuid.UUID) error

	// Object upload/download operations
	UploadObject(ctx context.Context, id uuid.UUID, reader io.Reader) error
	UploadObjectWithMetadata(ctx context.Context, reader io.Reader, req UploadObjectWithMetadataRequest) error
	DownloadObject(ctx context.Context, id uuid.UUID) (io.ReadCloser, error)
	GetUploadURL(ctx context.Context, id uuid.UUID) (string, error)
	GetDownloadURL(ctx context.Context, id uuid.UUID) (string, error)
	GetPreviewURL(ctx context.Context, id uuid.UUID) (string, error)

	// Object metadata operations
	SetObjectMetadata(ctx context.Context, objectID uuid.UUID, metadata map[string]interface{}) error
	GetObjectMetadata(ctx context.Context, objectID uuid.UUID) (map[string]interface{}, error)
	UpdateObjectMetaFromStorage(ctx context.Context, objectID uuid.UUID) (*ObjectMetadata, error)

	// Storage backend operations
	RegisterBackend(name string, backend BlobStore)
	GetBackend(name string) (BlobStore, error)
}

Service defines the main interface for the simple-content library

func New

func New(options ...Option) (Service, error)

New creates a new service instance with the given options

type SetContentMetadataRequest

type SetContentMetadataRequest struct {
	ContentID      uuid.UUID
	ContentType    string
	Title          string
	Description    string
	Tags           []string
	FileName       string
	FileSize       int64
	CreatedBy      string
	CustomMetadata map[string]interface{}
}

SetContentMetadataRequest contains parameters for setting content metadata

type StorageBackend

type StorageBackend struct {
	Name      string                 `json:"name"`
	Type      string                 `json:"type"`
	Config    map[string]interface{} `json:"config"`
	IsActive  bool                   `json:"is_active"`
	CreatedAt time.Time              `json:"created_at"`
	UpdatedAt time.Time              `json:"updated_at"`
}

StorageBackend represents a configurable storage backend

type StorageError

type StorageError struct {
	Backend string
	Key     string
	Op      string
	Err     error
}

StorageError represents an error related to storage operations

func (*StorageError) Error

func (e *StorageError) Error() string

func (*StorageError) Unwrap

func (e *StorageError) Unwrap() error

type UpdateContentRequest

type UpdateContentRequest struct {
	Content *Content
}

UpdateContentRequest contains parameters for updating content

type UploadObjectWithMetadataRequest

type UploadObjectWithMetadataRequest struct {
	ObjectID uuid.UUID
	MimeType string
}

UploadObjectWithMetadataRequest contains parameters for uploading object with metadata

type UploadParams

type UploadParams struct {
	ObjectKey string
	MimeType  string
}

UploadParams contains parameters for uploading an object

Directories

Path Synopsis
repo
storage
fs
s3

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL