jmapservicelibs

package module
v1.0.120 Latest Latest
Warning

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

Go to latest
Published: Mar 7, 2026 License: MIT Imports: 0 Imported by: 0

README

jmap-service-libs

Shared Go libraries for the jmap-service-* family of repositories.

Installation

go get github.com/jarrod-lowe/jmap-service-libs

Available Packages

tracing

OpenTelemetry/X-Ray instrumentation for JMAP services running on AWS Lambda.

import "github.com/jarrod-lowe/jmap-service-libs/tracing"

Features:

  • Automatic X-Ray and W3C Trace Context propagation
  • Standardized attribute helpers: RequestID, AccountID, BlobID, ParentBlobID, ContentType, Function, JMAPMethod, JMAPClientID, JMAPCallIndex
  • Span creation helpers: StartHandlerSpan, StartMethodSpan, StartColdStartSpan
  • Error recording with proper status codes: RecordError
  • Convenience tracer wrapper: Tracer
logging

Structured JSON logging for Lambda environments using slog.

import "github.com/jarrod-lowe/jmap-service-libs/logging"

// Simple usage - reads LOG_LEVEL from environment (DEBUG, INFO, WARN, ERROR)
var logger = logging.New()

// Override log level programmatically
var debugLogger = logging.New(logging.WithLevel(slog.LevelDebug))

// Capture output for testing
var buf bytes.Buffer
testLogger := logging.New(logging.WithOutput(&buf))

Features:

  • JSON output format for CloudWatch Logs
  • Environment-based log level via LOG_LEVEL (defaults to INFO)
  • Option pattern for overriding level or output
  • Zero dependencies beyond standard library
awsinit

Lambda initialization with OpenTelemetry tracing for AWS Lambda handlers.

import "github.com/jarrod-lowe/jmap-service-libs/awsinit"

// HTTP handler (API Gateway) - creates cold start span
result, err := awsinit.Init(context.Background(),
    awsinit.WithHTTPHandler("jmap-api"),
)
if err != nil {
    panic(err)
}
defer result.Cleanup()

ddb := dynamodb.NewFromConfig(result.Config)
handler := NewHandler(ddb)
result.Start(handler.Handle)

// Event-driven handler (SQS, SNS, etc.)
result, err := awsinit.Init(context.Background())
if err != nil {
    panic(err)
}
sqsClient := sqs.NewFromConfig(result.Config)
result.Start(handler.Handle)

Features:

  • Encapsulates Lambda bootstrap boilerplate (~15 lines → ~3 lines)
  • Automatic OTel tracing initialization with X-Ray
  • AWS config loading with OTel middleware
  • Cold start span for HTTP handlers
  • Handler instrumentation via otellambda
  • Distinct error types for debugging
jmaperror

Type-safe JMAP error handling per RFC 8620.

import "github.com/jarrod-lowe/jmap-service-libs/jmaperror"

// Method-level errors (returned in methodResponses)
err := jmaperror.InvalidArguments("mailboxId must be provided")
err := jmaperror.ServerFail("database error", underlyingErr)

// Set errors (per-object failures in Foo/set)
err := jmaperror.NotFound("object not found")
err := jmaperror.InvalidProperties("invalid values", []string{"name", "email"})

// HTTP problems (request-level failures as application/problem+json)
err := jmaperror.NotJSON("request body is not valid JSON")
err := jmaperror.Limit("maxSizeRequest", "request exceeds 10MB")

Features:

  • MethodError: UnknownMethod, InvalidArguments, ServerFail, AccountNotFound, InvalidResultReference, StateMismatch, Forbidden
  • SetError: NotFound, InvalidProperties, TooLarge, OverQuota, TooManyPending, BlobNotFound, InvalidMailboxId, InvalidEmail
  • HTTPProblem: UnknownCapability, NotJSON, NotRequest, Limit
  • Common JMAPError interface with Type() and ToMap() methods
  • Proper Go error wrapping with Unwrap() for ServerFail
dbclient

Shared DynamoDB client utilities for JMAP services.

import "github.com/jarrod-lowe/jmap-service-libs/dbclient"

// Create client from AWS config (integrates with awsinit)
result, _ := awsinit.Init(ctx, awsinit.WithHTTPHandler("my-handler"))
ddb := dbclient.NewClient(result.Config)
repo := myrepo.New(ddb, os.Getenv("TABLE_NAME"))

// Use key helpers
pk := dbclient.AccountPK("account-123")  // "ACCOUNT#account-123"
pk := dbclient.UserPK("user-456")        // "USER#user-456"

// Error handling
if dbclient.IsConditionalCheckFailed(err) {
    return ErrNotFound
}
if idx := dbclient.GetConditionalCheckFailureIndex(err); idx >= 0 {
    // Handle specific item failure in transaction
}

Features:

  • DynamoDBClient interface for testable repository dependencies
  • NewClient(cfg aws.Config) helper integrating with awsinit
  • Key constants: AttrPK, AttrSK, PrefixAccount, PrefixUser, SKMeta
  • Key helpers: AccountPK(id), UserPK(id)
  • Error helpers: IsConditionalCheckFailed, IsTransactionCanceled, GetTransactionCancellationReasons, HasConditionalCheckFailure, GetConditionalCheckFailureIndex
plugincontract

JMAP plugin communication types for the core-to-plugin invocation contract.

import "github.com/jarrod-lowe/jmap-service-libs/plugincontract"

func handler(ctx context.Context, req plugincontract.PluginInvocationRequest) (plugincontract.PluginInvocationResponse, error) {
    // Extract arguments using type-safe Args helpers
    accountID, _ := req.Args.String("accountId")
    ids, _ := req.Args.StringSlice("ids")
    limit := req.Args.IntOr("limit", 100)

    // Process request...

    return plugincontract.PluginInvocationResponse{
        MethodResponse: plugincontract.MethodResponse{
            Name:     req.Method,
            Args:     plugincontract.Args{"accountId": accountID, "list": results},
            ClientID: req.ClientID,
        },
    }, nil
}

Features:

  • PluginInvocationRequest - Request payload sent from core to plugin
  • PluginInvocationResponse - Response wrapper from plugin to core
  • MethodResponse - JMAP method response structure
  • EventPayload - System event payload delivered via SQS
  • Args type with helper methods: String, StringOr, Int, IntOr, Float, Bool, BoolOr, StringSlice, Object, Has

See docs/plugin-interface.md for the full plugin author guide.

Planned Migrations

The following code patterns have been identified across jmap-service-core and jmap-service-email as candidates for migration to this shared library.

High Priority — Identical or Near-Identical Code

These patterns exist in both repositories with minimal variation:

Package Description Source Locations
logging Structured JSON logging setup with slog.NewJSONHandler Done - see logging package
awsinit AWS SDK config loading with OTel middleware instrumentation Done - see awsinit package
jmaperror JMAP protocol error response formatting, standard error type constants (unknownMethod, invalidArguments, serverFail, etc.) Done - see jmaperror package
dbclient DynamoDB client interface definition, key prefix constants (ACCOUNT#, META#, etc.), conditional check error handling helpers Done - see dbclient package
plugincontract JMAP plugin invocation request/response types (PluginInvocationRequest, PluginInvocationResponse, MethodResponse) Done - see plugincontract package
apiresponse API Gateway proxy response formatting, HTTP error response helpers jmap-service-core/cmd/blob-upload/main.go, jmap-service-core/cmd/blob-download/main.go
Medium Priority — Similar Patterns Requiring Abstraction

These patterns are similar but need some generalization:

Package Description Source Locations
validation Media type (MIME) validation, AWS resource tag validation jmap-service-core/internal/bloballocate/allocate.go, jmap-service-core/cmd/blob-upload/main.go
arnutil ARN parsing, SQS ARN to Queue URL conversion jmap-service-core/cmd/account-init/main.go
httpclient HTTP client wrapper with exponential backoff retry jmap-service-email/internal/blob/client.go
sqspublish Generic SQS message publisher with JSON serialization jmap-service-email/internal/blobdelete/publisher.go, jmap-service-email/internal/mailboxcleanup/publisher.go
txerror DynamoDB TransactionCanceledException handling, conditional check failure detection and categorization Done - merged into dbclient package
Future Candidates — Currently in One Repository

These patterns exist in only one repo but are likely needed as more services are added:

Package Description Current Location Rationale
auth Account ID extraction from JWT claims and IAM path parameters, IAM authentication detection, ARN normalization, principal authorization jmap-service-core/cmd/*/main.go, jmap-service-core/internal/plugin/authorization.go Any service needing direct client authentication
resultref JMAP result reference resolution (RFC 8620 §3.7), JSON pointer evaluation with wildcard support jmap-service-core/internal/resultref/ Any service processing JMAP method calls
plugininvoke Lambda-based plugin invocation interface and implementation jmap-service-core/internal/plugin/invoker.go Core pattern for JMAP method dispatch
pluginregistry Plugin metadata registry, method-to-Lambda routing, capability management jmap-service-core/internal/plugin/registry.go Central to JMAP multi-service architecture
emailparse RFC 5322 email parsing, MIME structure extraction, body part handling jmap-service-email/internal/email/parser.go Any email-related service
headers Email header parsing (RFC 2047 decoding, address list parsing, date parsing) jmap-service-email/internal/headers/ Any email-related service
charset Character set detection and decoding for email body content jmap-service-email/internal/charset/ Any email-related service
keywords JMAP Email keyword validation per RFC 8621 jmap-service-email/internal/email/keywords.go Any Email/* method handler
Architectural Patterns (Document Only)

These patterns should be documented as best practices but don't require code extraction:

  • Lambda Dependency Injection — Handler struct with interface dependencies, initialized in main(), enables testability
  • Single-Table Design KeysPK() and SK() methods on domain types with consistent prefix constants
  • Repository Builder Pattern — Methods returning []types.TransactWriteItem for composable atomic transactions
  • Functional Mocks — Test mocks with optional function pointers for flexible per-test stubbing
  • SQS/DynamoDB Streams Consumer Pattern — Standard event loop with batch failure tracking

Development

Using Local Changes in Sibling Services

When developing changes to this library alongside a consuming service, use a replace directive in the consuming service's go.mod:

replace github.com/jarrod-lowe/jmap-service-libs => ../jmap-service-libs

Remember to remove the replace directive before committing.

Development Commands
make help     # Show all available targets
make deps     # Tidy dependencies
make test     # Run all tests
make lint     # Run golangci-lint
make fmt      # Format code
make clean    # Remove build artifacts

Contributing

When adding a new package:

  1. Create a directory at the root level (e.g., tracing/)
  2. Include comprehensive tests
  3. Add package documentation in a doc.go file
  4. Update this README with the package description

Documentation

Overview

Package jmapservicelibs provides shared libraries for the jmap-service-* family of repositories.

This module contains common functionality used across multiple JMAP services, including tracing, database clients, and other shared utilities.

Packages are organized at the root level for clean import paths:

import "github.com/jarrod-lowe/jmap-service-libs/tracing"
import "github.com/jarrod-lowe/jmap-service-libs/dbclient"

Directories

Path Synopsis
Package awsinit provides Lambda initialization boilerplate for AWS Lambda handlers with OpenTelemetry tracing integration.
Package awsinit provides Lambda initialization boilerplate for AWS Lambda handlers with OpenTelemetry tracing integration.
Package dbclient provides shared DynamoDB client utilities for JMAP services.
Package dbclient provides shared DynamoDB client utilities for JMAP services.
Package jmaperror provides type-safe JMAP error handling per RFC 8620.
Package jmaperror provides type-safe JMAP error handling per RFC 8620.
Package logging provides a configured slog.Logger for structured JSON logging in Lambda environments.
Package logging provides a configured slog.Logger for structured JSON logging in Lambda environments.
Package plugincontract defines the contract types for JMAP plugins.
Package plugincontract defines the contract types for JMAP plugins.
Package textproc provides text processing and chunking functionality for email content.
Package textproc provides text processing and chunking functionality for email content.
chain
Package chain provides composition for text processing pipeline.
Package chain provides composition for text processing pipeline.
chunker
Package chunker performs paragraph-based chunking.
Package chunker performs paragraph-based chunking.
combiner
Package combiner accumulates multiple chunks into ChunkSlices with configurable byte limits and overlap.
Package combiner accumulates multiple chunks into ChunkSlices with configurable byte limits and overlap.
elider
Package elider performs content elision.
Package elider performs content elision.
htmlstrip
Package htmlstrip removes HTML markup from input while preserving text content.
Package htmlstrip removes HTML markup from input while preserving text content.
reader
Package reader provides an adapter from io.Reader to BytesProcessor.
Package reader provides an adapter from io.Reader to BytesProcessor.
splitter
Package splitter performs size-based chunking for embedding preparation.
Package splitter performs size-based chunking for embedding preparation.
utf8clean
Package utf8clean ensures UTF-8 encoding in input data.
Package utf8clean ensures UTF-8 encoding in input data.
Package tracing provides OpenTelemetry instrumentation for JMAP services running on AWS Lambda with X-Ray.
Package tracing provides OpenTelemetry instrumentation for JMAP services running on AWS Lambda with X-Ray.

Jump to

Keyboard shortcuts

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