This file provides guidance to AI development agents (e.g., Claude Code, GitHub Copilot) when working with this repository.
Repository Overview
artsend is a pure email sending integration package for the Artalytics platform, providing transactional email functionality via the Resend API. This package follows a clean architecture with zero database dependencies.
Core Philosophy
Key Components
Email Sending (send_contact_email())
result <- artsend::send_contact_email(
to = "artist@example.com", # From caller's data
visitor_name = "John Doe",
visitor_email = "john@example.com",
message = "I love your work!",
artwork_title = "Sunset Dreams", # Optional context
artwork_url = "https://..." # Optional context
)Configuration (get_resend_config())
- Reads
ART_RESEND_KEYfrom environment - Validates API key format
- Returns configuration list
Development Workflow
Testing Strategy
# Unit tests (fast, no API calls)
devtools::test()
# Integration tests (requires API key)
Sys.setenv(ART_RESEND_KEY = "re_xxx")
devtools::test(filter = "integration")
# Check package
devtools::check()Adding New Email Types
-
Create Template Function in
R/email-template.R:
build_notification_email_html <- function(user_name, notification_text) {
# Build HTML template
}-
Create Sending Function in
R/send-*.R:
send_notification_email <- function(to, user_name, notification_text) {
# Validate, build, send
}-
Add Tests in
tests/testthat/test-*.R:
test_that("send_notification_email validates inputs", {
# Test validation
})- Document with Roxygen:
#' @export
send_notification_email <- function(...) { }- Regenerate Docs:
devtools::document()Environment Configuration
Resend Dashboard Setup
- Sign up at resend.com
- Verify sending domain (e.g., contact.artalytics.app)
- Generate API key
- Configure sender address (e.g., contact@artalytics.app)
Dependencies
Integration Patterns
In Shiny Applications (e.g., modGallery)
# In server logic
observeEvent(input$submit, {
# Email already in appdata - no DB query needed!
result <- artsend::send_contact_email(
to = r$appdata$artist$info$email,
visitor_name = input$name,
visitor_email = input$email,
message = input$message,
artwork_title = r$appdata$artwork$info$full$title
)
if (result$success) {
showNotification("Email sent!")
} else {
showNotification(paste("Error:", result$error), type = "error")
}
})In Batch Processing
# Send multiple emails
results <- lapply(recipients, function(recipient) {
artsend::send_contact_email(
to = recipient$email,
visitor_name = recipient$name,
# ...
)
})Error Handling Pattern
result <- artsend::send_contact_email(...)
if (!result$success) {
# Log error
logger::log_error("Email failed: {result$error}")
# Notify user
shiny::showNotification(
"Failed to send email. Please try again.",
type = "error"
)
# Could queue for retry, etc.
}Testing Approach
Unit Tests (Fast, No API)
test_that("validates email format", {
expect_true(validate_email_format("user@example.com"))
expect_false(validate_email_format("invalid"))
})Integration Tests (Require API Key)
test_that("sends email via Resend", {
skip_on_ci()
skip_if_not(is_resend_configured())
result <- send_contact_email(...)
expect_true(result$success)
})Mocking for Unit Tests
test_that("handles API errors gracefully", {
mockery::stub(
send_contact_email,
"httr2::req_perform",
stop("API error")
)
result <- send_contact_email(...)
expect_false(result$success)
})Security Considerations
HTML Escaping
All user inputs are escaped using htmltools::htmlEscape() to prevent XSS:
visitor_name_safe <- htmltools::htmlEscape(visitor_name)
message_formatted <- htmltools::htmlEscape(message)Common Development Tasks
Add New Email Type
- Create template function
- Create sending function
- Add validation
- Write tests
- Document
- Export
Update HTML Template
- Edit
build_contact_email_html()inR/email-template.R - Test rendering with sample data
- Check mobile responsiveness
- Verify HTML escaping
Debug Email Sending
# Enable verbose logging
options(httr2_verbose = TRUE)
# Test with debug output
result <- artsend::send_contact_email(...)
print(result)Change API Provider
If switching from Resend to another service: 1. Update get_resend_config() → get_email_config() 2. Update API endpoint in send_contact_email() 3. Update payload structure for new API 4. Update tests 5. Update environment variable names
Performance Considerations
Email Sending is Synchronous
- Each call blocks until API responds
- Consider async for batch operations
- Add timeout handling for production
Troubleshooting
Common Issues
Email not sending: - Check ART_RESEND_KEY is set - Verify API key is valid in Resend dashboard - Check sender domain is verified - Review Resend API status
Invalid email format errors: - Ensure all emails pass validation - Check for whitespace or special chars - Verify email parameters are non-empty
HTML rendering issues: - Test template with various email clients - Verify HTML escaping is working - Check for inline CSS issues
Debug Tools
# Check configuration
artsend::is_resend_configured()
artsend::get_resend_config()
# Validate inputs
artsend::validate_email_format("test@example.com")
# Test with verbose HTTP
options(httr2_verbose = TRUE)This package provides pure email sending functionality - keep it simple, testable, and dependency-free!
