tags

package
v0.7.6 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

TagsService

A flexible tagging service supporting pure labels, metadata tags (name-value pairs), and multi-user tagging for any entity.

Features

  • Pure Tags: Simple labels like "Favorites", "Rock", "To Read"
  • Metadata Tags: Name-value pairs like venue="Wembley Stadium", year="2024"
  • Multi-User Tagging: Multiple users can apply the same tag to an entity
  • Tag Ownership: Tags belong to users/organizations with privacy scopes
  • Denormalized Counts: Fast access via pre-aggregated usage counts
  • Inline Tag Creation: Create tags on-the-fly when tagging entities
  • Backend Agnostic: Works with GORM (PostgreSQL/MySQL/SQLite) and Google Cloud Datastore

Architecture

TagsService Interface (Public API)
    |
BaseTagsService (Shared logic + caching)
    |                    |
gRPC Server          Connect RPC Adapter
(embedded)           (ConnectTagsServer)
    |
TagsStorageProvider Interface (Storage abstraction)
    |
Concrete Backends
    +-- GORMTagsService (SQL databases)
    +-- DatastoreTagsService (Google Cloud)
gRPC and Connect Support

The service is directly registrable as a gRPC server and includes a Connect RPC adapter:

import (
    "github.com/panyam/goapplib/content/services/tags"
    "github.com/panyam/goapplib/content/services/tags/backends"
    tagsv1 "github.com/panyam/goapplib/content/gen/go/tags/v1"
    "github.com/panyam/goapplib/content/gen/go/tags/v1/tagsv1connect"
)

// Create service
service := backends.NewGORMTagsService(db)

// Register as gRPC server
grpcServer := grpc.NewServer()
tagsv1.RegisterTagsServiceServer(grpcServer, service)

// Register as Connect handler (HTTP/2 + JSON)
mux := http.NewServeMux()
path, handler := tagsv1connect.NewTagsServiceHandler(tags.NewConnectTagsServer(service))
mux.Handle(path, handler)

Quick Start

GORM Backend (PostgreSQL/MySQL/SQLite)
import (
    "github.com/panyam/goapplib/content/services/tags/backends"
    "gorm.io/gorm"
)

// Create service
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
service := backends.NewGORMTagsService(db)

// Auto-migrate tables
service.AutoMigrate()

// Create a tag
createResp, err := service.CreateTag(ctx, &v1.CreateTagRequest{
    Value:     "Rock",
    OwnerId:   "user-123",
    Scope:     v1.TagScope_TAG_SCOPE_PRIVATE,
})

// Tag an entity
tagResp, err := service.TagEntity(ctx, &v1.TagEntityRequest{
    EntityId:   "song-456",
    TagId:      createResp.Tag.Id,
    TaggedBy:   "user-123",
})
Datastore Backend (Google Cloud)
import (
    "cloud.google.com/go/datastore"
    "github.com/panyam/goapplib/content/services/tags/backends"
)

client, _ := datastore.NewClient(ctx, "my-project")
service := backends.NewDatastoreTagsService(client, "my-namespace")

// Create a tag with inline tagging
tagResp, err := service.TagEntity(ctx, &v1.TagEntityRequest{
    EntityId:   "book-123",
    Value:      "To Read",
    OwnerId:    "user-456",
    TaggedBy:   "user-456",
})

API Reference

Tag Management
Method Description
CreateTag Create a new tag (returns existing if duplicate)
GetTag Get a tag by ID
UpdateTag Update tag properties
DeleteTag Soft-delete a tag
ListTags List tags for an owner
SearchTags Search tags by prefix
Entity Tagging
Method Description
TagEntity Apply a tag to an entity (with optional inline creation)
UntagEntity Remove a tag from an entity
GetEntityTags Get all tags for an entity
Advanced Operations
Method Description
MergeTags Merge multiple tags into one
PromoteTag Promote a private tag to shared/public
GetPopularTags Get most-used tags

Tag Types

Pure Tags (Labels)

Simple labels without a name component:

// Creates a tag with value="Favorites", name=""
service.CreateTag(ctx, &v1.CreateTagRequest{
    Value:     "Favorites",
    OwnerId:   "user-123",
})
Metadata Tags (Name-Value Pairs)

Structured tags with both name and value:

// Creates a tag with name="venue", value="Wembley Stadium"
service.CreateTag(ctx, &v1.CreateTagRequest{
    Name:      "venue",
    Value:     "Wembley Stadium",
    OwnerId:   "user-123",
})

Tag Scopes

Scope Description
TAG_SCOPE_PRIVATE Only visible to the owner
TAG_SCOPE_SHARED Visible to organization members
TAG_SCOPE_PUBLIC Visible to everyone

Multi-User Tagging

Multiple users can apply the same tag to the same entity. Each application is tracked separately via the tagged_by field:

// User 1 tags a book
service.TagEntity(ctx, &v1.TagEntityRequest{
    EntityId:   "book-123",
    TagId:      tagID,
    TaggedBy:   "user-1",
})

// User 2 also tags the same book with the same tag
service.TagEntity(ctx, &v1.TagEntityRequest{
    EntityId:   "book-123",
    TagId:      tagID,
    TaggedBy:   "user-2",
})

// User 1 removes their tag (User 2's tag remains)
service.UntagEntity(ctx, &v1.UntagEntityRequest{
    EntityId:   "book-123",
    TagId:      tagID,
    TaggedBy:   "user-1",
})

Data Models

Tag
message Tag {
  string id = 1;
  string owner_id = 3;
  string name = 4;             // For metadata tags (optional)
  string normalized_name = 5;
  string value = 6;            // The tag label
  string normalized_value = 7; // Lowercase for deduplication
  TagScope scope = 8;
  TagState state = 9;
  int64 usage_count = 10;      // Denormalized count
  // timestamps...
}
EntityTag
message EntityTag {
  string id = 1;
  string tag_id = 2;
  string entity_type = 3;    // "post", "song", "book"
  string entity_id = 4;
  string tagged_by = 5;      // User who applied this tag
  EntityTagVisibility visibility = 6;
  // timestamps...
}

Database Schema

GORM Tables
  • tags - Tag definitions with composite unique index on (owner_id, normalized_name, normalized_value)
  • entity_tags - Tag applications with composite unique index on (tag_id, entity_type, entity_id, tagged_by)
Datastore Kinds
  • Tag - Tag definition entities
  • EntityTag - Tag application entities with composite key
Datastore Indexes

The Datastore backend requires composite indexes for complex queries. Enable validation at construction:

import dsidx "github.com/panyam/goapplib/datastore"

// Validate indexes during construction (recommended for production)
service, err := backends.NewDatastoreTagsService(client, namespace,
    dsidx.WithValidation(ctx))
if err != nil {
    // Error includes missing indexes + gcloud command to fix
    // Also writes tags_index.yaml automatically
    log.Fatal(err)
}

// Or skip validation (e.g., for emulator)
service, err := backends.NewDatastoreTagsService(client, namespace)

To deploy indexes manually:

gcloud datastore indexes create tags_index.yaml

Integration Testing

Running Tests with SQLite (Default)
cd content
go test ./tests/gorm/... -run TestTagsService
Running Tests with PostgreSQL
# Start PostgreSQL
make updb

# Run tests
make testpg

# Stop PostgreSQL
make downdb
Running Tests with Datastore Emulator
# Start emulator
make upds

# Run tests
make testds

# Stop emulator
make downds
Running Tests with Real Datastore
make testrealDS DS_REAL_PROJECT=my-project DS_REAL_CREDENTIALS=~/creds.json

Best Practices

  1. Use Normalized Values: Tags are deduplicated using normalized (lowercase) values. "Rock" and "ROCK" are the same tag.

  2. Choose Appropriate Scope: Start with private tags and promote to shared/public as needed.

  3. Use Inline Creation: When tagging entities, use the inline creation feature to create tags on-the-fly:

    service.TagEntity(ctx, &v1.TagEntityRequest{
        EntityId:   "book-123",
        Value:      "To Read",  // Creates tag if not exists
        OwnerId:    "user-456",
        TaggedBy:   "user-456",
    })
    
  4. Leverage Metadata Tags: For structured data like locations, dates, or custom attributes, use name-value pairs instead of encoding data in plain tag values.

Design Notes

The Tag model uses name instead of key for the metadata identifier field. This avoids a naming collision in the Datastore generator where proto field key becomes Go field Key, conflicting with the auto-generated Key *datastore.Key field.

See Also

Documentation

Overview

Package tags provides Connect RPC adapters for the TagsService.

Package tags provides a reusable tagging and metadata service for any entity. The service supports both pure tags (labels) and metadata (name-value pairs) with denormalized counts for fast reads.

Architecture

The package follows a layered architecture:

  • TagsService interface: defines the contract for tag operations
  • BaseTagsService: provides shared logic (normalization, caching, common operations)
  • TagsStorageProvider: abstracts raw storage operations
  • Backend implementations (gorm, gae): concrete storage providers

Usage

Applications should use one of the concrete backend implementations:

// GORM backend (for PostgreSQL/MySQL/SQLite)
tagsService := gorm.NewTagsService(db)

// GAE Datastore backend (for Google Cloud)
tagsService := gae.NewTagsService(client, "namespace")

Key Features

  • Pure tags: Simple labels like "favorites", "rock"
  • Metadata: Name-value pairs like venue="Wembley", rating="5"
  • Multi-user tagging: Same tag can be applied by multiple users
  • Visibility control: Private, shared, and public tag applications
  • Tag deduplication via normalization
  • Denormalized usage counts

Index

Constants

View Source
const DefaultUserIDContextKey = common.DefaultUserIDContextKey

DefaultUserIDContextKey is the default context key for the authenticated user ID. Can be overridden per service instance via WithUserIDContextKey. Uses the common default so all goapplib services share the same key.

Variables

View Source
var (
	ErrTagIDRequired    = &TagsError{Message: "tag id is required"}
	ErrValueRequired    = &TagsError{Message: "value is required"}
	ErrEntityRequired   = &TagsError{Message: "entity_id is required"}
	ErrTaggedByRequired = &TagsError{Message: "tagged_by is required"}
	ErrTagNotFound      = &TagsError{Message: "tag not found"}
	ErrTagMigrating     = &TagsError{Message: "tag is currently being migrated, new applications not allowed"}
	ErrTargetTagExists  = &TagsError{Message: "target tag already exists, set merge_if_exists=true to merge"}
)

Error types

Functions

func DefaultNormalizer

func DefaultNormalizer(s string) string

DefaultNormalizer is the default normalization function. It lowercases and trims whitespace.

func GetMountedEntityID

func GetMountedEntityID(ctx context.Context) string

GetMountedEntityID retrieves the entity ID from mount context. This is set by the mount middleware when the service is mounted at a path like /songs/{songId}/tags.

func GetUserIDFromContext

func GetUserIDFromContext(ctx context.Context, key any) string

GetUserIDFromContext returns the user ID from context using the given key. Returns empty string if not set.

func WithMountedEntityID

func WithMountedEntityID(ctx context.Context, entityID string) context.Context

WithMountedEntityID stores the entity ID in context. This is typically called by mount middleware after extracting the path parameter.

func WithUserID

func WithUserID(ctx context.Context, key any, userID string) context.Context

WithUserID returns a new context with the user ID set. Typically called by interceptors/middleware.

Types

type AfterEntityTagDeleteHook

type AfterEntityTagDeleteHook func(ctx context.Context, entityTag *v1.EntityTag) error

AfterEntityTagDeleteHook is called after successfully deleting an entity tag association. Errors from this hook are logged but don't affect the response.

type AfterEntityTagSaveHook

type AfterEntityTagSaveHook func(ctx context.Context, entityTag *v1.EntityTag, tag *v1.Tag) error

AfterEntityTagSaveHook is called after successfully saving an entity tag association. Errors from this hook are logged but don't affect the response.

type AfterTagDeleteHook

type AfterTagDeleteHook func(ctx context.Context, tag *v1.Tag) error

AfterTagDeleteHook is called after successfully deleting a tag. Errors from this hook are logged but don't affect the response.

type AfterTagSaveHook

type AfterTagSaveHook func(ctx context.Context, tag *v1.Tag) error

AfterTagSaveHook is called after successfully saving a tag. Errors from this hook are logged but don't affect the response.

type AfterTagsReadHook

type AfterTagsReadHook func(ctx context.Context, tags []*v1.Tag) error

AfterTagsReadHook is called after reading tags (for transformation/enrichment). Can modify the tags slice in place.

type AuthorizeHook

type AuthorizeHook func(ctx context.Context, hookCtx *HookContext) error

AuthorizeHook is called before any operation to check authorization. The hook can: - Return an error to deny the operation - Modify the request via hookCtx.Request (e.g., set OwnerId from auth context) - Read/validate request fields

type BaseTagsService

type BaseTagsService struct {
	v1.UnimplementedTagsServiceServer
	StorageProvider TagsStorageProvider

	// Optional in-memory cache for tag lookups
	CacheEnabled bool
	TagCache     map[string]*v1.Tag // key: tag ID
	CacheMu      sync.RWMutex

	// Normalizer function (defaults to lowercase + trim)
	Normalizer func(string) string

	// UserIDContextKey is the context key for reading user ID.
	// Defaults to DefaultUserIDContextKey if not set.
	UserIDContextKey any

	// Hooks for customization
	Hooks Hooks
}

BaseTagsService provides shared logic for tags services. Embeds UnimplementedTagsServiceServer for gRPC forward compatibility.

func NewBaseTagsService

func NewBaseTagsService(provider TagsStorageProvider, opts ...ServiceOption) *BaseTagsService

NewBaseTagsService creates a new BaseTagsService with the given storage provider.

func (*BaseTagsService) BatchGetEntityTags

BatchGetEntityTags returns tags for multiple entities at once.

func (*BaseTagsService) BatchTagEntities

BatchTagEntities applies a tag to multiple entities at once.

func (*BaseTagsService) ConnectHandler

func (s *BaseTagsService) ConnectHandler(ctx context.Context, opts ...common.HandlerOption) (string, http.Handler)

ConnectHandler returns an http.Handler for Connect RPC. Must be mounted at a path that prefixes the Connect procedure paths.

Example: Mount at /songs/{songId}/rpc/ to handle:

  • POST /songs/123/rpc/content.tags.v1.TagsService/TagEntity

Usage:

path, handler := tagsService.ConnectHandler(ctx,
    common.WithEntityParam("songId"),
)
router.Handle("/songs/{songId}/rpc/", handler)

func (*BaseTagsService) ConnectHandlerWithOptions

func (s *BaseTagsService) ConnectHandlerWithOptions(ctx context.Context, connectOpts []connect.HandlerOption, mountOpts ...common.HandlerOption) (string, http.Handler)

ConnectHandlerWithOptions returns an http.Handler for Connect RPC with additional connect options.

func (*BaseTagsService) CreateTag

CreateTag creates a new tag or returns existing if duplicate.

func (*BaseTagsService) DeleteTag

DeleteTag removes a tag and optionally untags all entities.

func (*BaseTagsService) GetEntitiesWithTag

GetEntitiesWithTag returns all entities that have a specific tag.

func (*BaseTagsService) GetEntityTags

GetEntityTags returns all tags for a specific entity.

func (*BaseTagsService) GetPopularTags

GetPopularTags returns the most used tags.

func (*BaseTagsService) GetTag

GetTag retrieves a tag by ID.

func (*BaseTagsService) InitializeCache

func (s *BaseTagsService) InitializeCache()

InitializeCache sets up the in-memory cache.

func (*BaseTagsService) ListTags

ListTags lists tags for an owner with filtering.

func (*BaseTagsService) MergeTags

MergeTags merges two tags into one (admin operation).

func (*BaseTagsService) PromoteTag

PromoteTag promotes a private tag to public scope.

func (*BaseTagsService) RESTHandler

func (s *BaseTagsService) RESTHandler(ctx context.Context, opts ...common.HandlerOption) http.Handler

RESTHandler returns an http.Handler for REST endpoints with a default mux. For more control over the mux configuration, use RegisterRESTHandlers instead.

Usage:

router.Handle("/songs/{songId}/tags/", tagsService.RESTHandler(ctx,
    common.WithEntityParam("songId"),
))

func (*BaseTagsService) RegisterRESTHandlers

func (s *BaseTagsService) RegisterRESTHandlers(ctx context.Context, mux *runtime.ServeMux) error

RegisterRESTHandlers registers the tags service REST handlers with the given gRPC-gateway mux. The caller provides the mux so they can configure it with their own middleware, marshalers, etc.

Example: Mount at /songs/{songId}/tags/ to handle:

  • POST /songs/123/tags/entity -> TagEntity with entity_id=123
  • GET /songs/123/tags/entity/123 -> GetEntityTags for entity_id=123

Usage:

mux := runtime.NewServeMux(/* your options */)
tagsService.RegisterRESTHandlers(ctx, mux)
router.Handle("/songs/{songId}/tags/", common.WrapWithEntityExtraction("songId", mux))

func (*BaseTagsService) SearchTags

SearchTags searches for tags by query (prefix match on value).

func (*BaseTagsService) TagEntity

TagEntity applies a tag to an entity.

func (*BaseTagsService) UntagEntity

UntagEntity removes a tag from an entity.

func (*BaseTagsService) UpdateTag

UpdateTag updates a tag's properties.

type BeforeEntityTagDeleteHook

type BeforeEntityTagDeleteHook func(ctx context.Context, entityTag *v1.EntityTag) error

BeforeEntityTagDeleteHook is called before deleting an entity tag association. Return an error to abort the delete.

type BeforeEntityTagSaveHook

type BeforeEntityTagSaveHook func(ctx context.Context, entityTag *v1.EntityTag, tag *v1.Tag) error

BeforeEntityTagSaveHook is called before saving an entity tag association. Return an error to abort the save.

type BeforeTagDeleteHook

type BeforeTagDeleteHook func(ctx context.Context, tag *v1.Tag) error

BeforeTagDeleteHook is called before deleting a tag. Return an error to abort the delete.

type BeforeTagSaveHook

type BeforeTagSaveHook func(ctx context.Context, tag *v1.Tag) error

BeforeTagSaveHook is called before saving a tag. The hook can modify the tag before it's saved. Return an error to abort the save.

type ConnectTagsServer

type ConnectTagsServer struct {
	Service TagsService
}

ConnectTagsServer wraps TagsService for Connect RPC. Use with tagsv1connect.NewTagsServiceHandler().

func NewConnectTagsServer

func NewConnectTagsServer(svc TagsService) *ConnectTagsServer

NewConnectTagsServer creates a new Connect RPC server wrapping the given TagsService.

func (*ConnectTagsServer) BatchGetEntityTags

BatchGetEntityTags returns tags for multiple entities at once.

func (*ConnectTagsServer) BatchTagEntities

BatchTagEntities applies a tag to multiple entities at once.

func (*ConnectTagsServer) CreateTag

CreateTag creates a new tag definition.

func (*ConnectTagsServer) DeleteTag

DeleteTag removes a tag and optionally untags all entities.

func (*ConnectTagsServer) GetEntitiesWithTag

GetEntitiesWithTag returns all entities that have a specific tag.

func (*ConnectTagsServer) GetEntityTags

GetEntityTags returns all tags for a specific entity.

func (*ConnectTagsServer) GetPopularTags

GetPopularTags returns the most used tags.

func (*ConnectTagsServer) GetTag

GetTag retrieves a tag by ID.

func (*ConnectTagsServer) ListTags

ListTags lists tags for an owner with filtering.

func (*ConnectTagsServer) MergeTags

MergeTags merges two tags into one (admin operation).

func (*ConnectTagsServer) PromoteTag

PromoteTag promotes a private tag to public.

func (*ConnectTagsServer) SearchTags

SearchTags searches for tags by name (autocomplete).

func (*ConnectTagsServer) TagEntity

TagEntity applies a tag to an entity.

func (*ConnectTagsServer) UntagEntity

UntagEntity removes a tag from an entity.

func (*ConnectTagsServer) UpdateTag

UpdateTag updates a tag's properties.

type EntityTagsOptions

type EntityTagsOptions struct {
	NameFilter string
	OwnerID    string
}

EntityTagsOptions contains options for listing entity tags.

type Event

type Event struct {
	Type     EventType
	TagID    string
	EntityID string
	UserID   string
	Tag      *v1.Tag
	// For merge operations
	SourceTagID string
	TargetTagID string
	// Count of affected entities
	EntitiesAffected int64
}

Event represents a tags service event for notifications.

type EventType

type EventType string

EventType represents the type of event that occurred.

const (
	EventTagCreated     EventType = "tag.created"
	EventTagUpdated     EventType = "tag.updated"
	EventTagDeleted     EventType = "tag.deleted"
	EventEntityTagged   EventType = "entity.tagged"
	EventEntityUntagged EventType = "entity.untagged"
	EventTagsMerged     EventType = "tags.merged"
	EventTagPromoted    EventType = "tag.promoted"
)

type HookContext

type HookContext struct {
	// Operation being performed (e.g., "CreateTag", "TagEntity")
	Operation string
	// UserID of the user performing the operation (may be empty, hook can set it)
	UserID string
	// EntityID being operated on (for entity tagging operations)
	EntityID string
	// TagID being operated on
	TagID string
	// Request is the original request object (type assert to access specific fields).
	// The hook can modify request fields (e.g., set OwnerId from auth context).
	Request any
}

HookContext provides context information for hook invocations.

type Hooks

type Hooks struct {
	// Authorization hook - called before operations
	OnAuthorize AuthorizeHook

	// Entity validation hook - validates entity exists
	ValidateEntity ValidateEntityHook

	// Tag lifecycle hooks
	BeforeTagSave   BeforeTagSaveHook
	AfterTagSave    AfterTagSaveHook
	BeforeTagDelete BeforeTagDeleteHook
	AfterTagDelete  AfterTagDeleteHook

	// EntityTag lifecycle hooks
	BeforeEntityTagSave   BeforeEntityTagSaveHook
	AfterEntityTagSave    AfterEntityTagSaveHook
	BeforeEntityTagDelete BeforeEntityTagDeleteHook
	AfterEntityTagDelete  AfterEntityTagDeleteHook

	// Read hooks
	AfterTagsRead AfterTagsReadHook

	// Event hooks
	OnEvent OnEventHook
}

Hooks contains all hook functions for the tags service.

type ListTagsOptions

type ListTagsOptions struct {
	OwnerID    string
	Scope      v1.TagScope
	NameFilter string // "" = all, empty string = pure tags, "venue" = specific name
	OrderBy    string // "name", "usage_count", "created_at", "display_order"
	Limit      int
	Offset     int
}

ListTagsOptions contains options for listing tags.

type OnEventHook

type OnEventHook func(ctx context.Context, event *Event) error

OnEventHook is called when notable events occur (for notifications, analytics, etc.). Errors from this hook are logged but don't affect the response.

type PopularTagsOptions

type PopularTagsOptions struct {
	NameFilter    string
	OwnerID       string
	IncludeShared bool
	Limit         int
}

PopularTagsOptions contains options for getting popular tags.

type SearchTagsOptions

type SearchTagsOptions struct {
	Query         string
	NameFilter    string
	OwnerID       string
	IncludeShared bool
	Limit         int
}

SearchTagsOptions contains options for searching tags.

type ServiceOption

type ServiceOption func(*BaseTagsService)

ServiceOption is a functional option for configuring the tags service.

func WithAfterEntityTagDelete

func WithAfterEntityTagDelete(hook AfterEntityTagDeleteHook) ServiceOption

WithAfterEntityTagDelete sets the after entity tag delete hook.

func WithAfterEntityTagSave

func WithAfterEntityTagSave(hook AfterEntityTagSaveHook) ServiceOption

WithAfterEntityTagSave sets the after entity tag save hook.

func WithAfterTagDelete

func WithAfterTagDelete(hook AfterTagDeleteHook) ServiceOption

WithAfterTagDelete sets the after tag delete hook.

func WithAfterTagSave

func WithAfterTagSave(hook AfterTagSaveHook) ServiceOption

WithAfterTagSave sets the after tag save hook.

func WithAfterTagsRead

func WithAfterTagsRead(hook AfterTagsReadHook) ServiceOption

WithAfterTagsRead sets the after tags read hook.

func WithBeforeEntityTagDelete

func WithBeforeEntityTagDelete(hook BeforeEntityTagDeleteHook) ServiceOption

WithBeforeEntityTagDelete sets the before entity tag delete hook.

func WithBeforeEntityTagSave

func WithBeforeEntityTagSave(hook BeforeEntityTagSaveHook) ServiceOption

WithBeforeEntityTagSave sets the before entity tag save hook.

func WithBeforeTagDelete

func WithBeforeTagDelete(hook BeforeTagDeleteHook) ServiceOption

WithBeforeTagDelete sets the before tag delete hook.

func WithBeforeTagSave

func WithBeforeTagSave(hook BeforeTagSaveHook) ServiceOption

WithBeforeTagSave sets the before tag save hook.

func WithCache

func WithCache() ServiceOption

WithCache enables the in-memory tag cache.

func WithHooks

func WithHooks(hooks Hooks) ServiceOption

WithHooks sets all hooks at once.

func WithNormalizer

func WithNormalizer(normalizer func(string) string) ServiceOption

WithNormalizer sets a custom normalizer function.

func WithOnAuthorize

func WithOnAuthorize(hook AuthorizeHook) ServiceOption

WithOnAuthorize sets the authorization hook.

func WithOnEvent

func WithOnEvent(hook OnEventHook) ServiceOption

WithOnEvent sets the event notification hook.

func WithUserIDContextKey

func WithUserIDContextKey(key any) ServiceOption

WithUserIDContextKey sets the context key used to read user ID from context. This allows different apps to use their own context key conventions.

func WithValidateEntity

func WithValidateEntity(hook ValidateEntityHook) ServiceOption

WithValidateEntity sets the entity validation hook.

type TagsError

type TagsError struct {
	Message string
}

TagsError represents an error in the tags service.

func (*TagsError) Error

func (e *TagsError) Error() string

type TagsService

type TagsService interface {
	// Tag CRUD
	CreateTag(ctx context.Context, req *v1.CreateTagRequest) (*v1.CreateTagResponse, error)
	GetTag(ctx context.Context, req *v1.GetTagRequest) (*v1.GetTagResponse, error)
	UpdateTag(ctx context.Context, req *v1.UpdateTagRequest) (*v1.UpdateTagResponse, error)
	DeleteTag(ctx context.Context, req *v1.DeleteTagRequest) (*v1.DeleteTagResponse, error)
	ListTags(ctx context.Context, req *v1.ListTagsRequest) (*v1.ListTagsResponse, error)

	// Entity tagging
	TagEntity(ctx context.Context, req *v1.TagEntityRequest) (*v1.TagEntityResponse, error)
	UntagEntity(ctx context.Context, req *v1.UntagEntityRequest) (*v1.UntagEntityResponse, error)
	GetEntityTags(ctx context.Context, req *v1.GetEntityTagsRequest) (*v1.GetEntityTagsResponse, error)
	GetEntitiesWithTag(ctx context.Context, req *v1.GetEntitiesWithTagRequest) (*v1.GetEntitiesWithTagResponse, error)

	// Batch operations
	BatchTagEntities(ctx context.Context, req *v1.BatchTagEntitiesRequest) (*v1.BatchTagEntitiesResponse, error)
	BatchGetEntityTags(ctx context.Context, req *v1.BatchGetEntityTagsRequest) (*v1.BatchGetEntityTagsResponse, error)

	// Discovery
	SearchTags(ctx context.Context, req *v1.SearchTagsRequest) (*v1.SearchTagsResponse, error)
	GetPopularTags(ctx context.Context, req *v1.GetPopularTagsRequest) (*v1.GetPopularTagsResponse, error)

	// Admin operations
	MergeTags(ctx context.Context, req *v1.MergeTagsRequest) (*v1.MergeTagsResponse, error)
	PromoteTag(ctx context.Context, req *v1.PromoteTagRequest) (*v1.PromoteTagResponse, error)
}

TagsService defines the interface for tag operations.

type TagsStorageProvider

type TagsStorageProvider interface {
	// Tag operations
	SaveTag(ctx context.Context, tag *v1.Tag) error
	GetTag(ctx context.Context, id string) (*v1.Tag, error)
	DeleteTag(ctx context.Context, id string) error
	ListTags(ctx context.Context, opts ListTagsOptions) ([]*v1.Tag, int, error)
	FindTagByNormalizedValues(ctx context.Context, ownerID, normalizedName, normalizedValue string) (*v1.Tag, error)
	SearchTags(ctx context.Context, opts SearchTagsOptions) ([]*v1.Tag, error)
	GetPopularTags(ctx context.Context, opts PopularTagsOptions) ([]*v1.Tag, error)

	// EntityTag operations
	SaveEntityTag(ctx context.Context, entityTag *v1.EntityTag) error
	DeleteEntityTag(ctx context.Context, tagID, entityID, taggedBy string) error
	GetEntityTag(ctx context.Context, tagID, entityID, taggedBy string) (*v1.EntityTag, error)
	ListEntityTagsByEntity(ctx context.Context, entityID string, opts EntityTagsOptions) ([]*v1.EntityTag, error)
	ListEntityTagsByTag(ctx context.Context, tagID string, limit, offset int) ([]*v1.EntityTag, int, error)
	DeleteEntityTagsByTag(ctx context.Context, tagID string) (int64, error)

	// TagUsageCounts operations (optional - for denormalized counts)
	GetTagUsageCounts(ctx context.Context, entityID string) (*v1.TagUsageCounts, error)
	SaveTagUsageCounts(ctx context.Context, counts *v1.TagUsageCounts) error
}

TagsStorageProvider is implemented by concrete backends (gorm, gae) to provide raw storage operations for tags.

type ValidateEntityHook

type ValidateEntityHook func(ctx context.Context, entityID string) error

ValidateEntityHook is called to validate that an entity exists and is valid. Return an error if the entity doesn't exist or is invalid.

Directories

Path Synopsis
Package backends provides concrete storage implementations for the tags service.
Package backends provides concrete storage implementations for the tags service.

Jump to

Keyboard shortcuts

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