services

package
v0.0.0-...-c75bb4f Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2025 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package services provides AT Protocol integration for leaflet.pub

Document Flow:

  • Pull: Fetch pub.leaflet.document records from AT Protocol repository
  • Post: Create new pub.leaflet.document records in AT Protocol repository
  • Push: Update existing pub.leaflet.document records in AT Protocol repository
  • Delete: Remove pub.leaflet.document records from AT Protocol repository

Movies & TV: Rotten Tomatoes with colly

Music: Album of the Year with chromedp

Books: OpenLibrary API

Index

Constants

View Source
const (
	// Open Library API endpoints
	OpenLibraryBaseURL string = "https://openlibrary.org"
)

Variables

View Source
var ExtractMovieMetadata = func(r io.Reader) (*Movie, error) {
	doc, err := goquery.NewDocumentFromReader(r)
	if err != nil {
		return nil, err
	}
	var movie Movie
	found := false
	doc.Find("script[type='application/ld+json']").Each(func(i int, s *goquery.Selection) {
		var tmp map[string]any
		if err := json.Unmarshal([]byte(s.Text()), &tmp); err == nil {
			if t, ok := tmp["@type"].(string); ok && t == "Movie" {
				if err := json.Unmarshal([]byte(s.Text()), &movie); err == nil {
					found = true
				}
			}
		}
	})
	if !found {
		return nil, fmt.Errorf("no Movie JSON-LD found")
	}
	return &movie, nil
}
View Source
var ExtractTVSeasonMetadata = func(r io.Reader) (*TVSeason, error) {
	doc, err := goquery.NewDocumentFromReader(r)
	if err != nil {
		return nil, err
	}
	var season TVSeason
	found := false
	doc.Find("script[type='application/ld+json']").Each(func(i int, s *goquery.Selection) {
		var tmp map[string]any
		if err := json.Unmarshal([]byte(s.Text()), &tmp); err == nil {
			if t, ok := tmp["@type"].(string); ok && t == "TVSeason" {
				if err := json.Unmarshal([]byte(s.Text()), &season); err == nil {
					found = true
				}
			}
		}
	})
	if !found {
		return nil, fmt.Errorf("no TVSeason JSON-LD found")
	}

	if season.SeasonNumber == 0 {
		if season.URL != "" {
			parts := strings.SplitSeq(season.URL, "/")
			for part := range parts {
				if strings.HasPrefix(part, "s") && len(part) > 1 {
					if num, err := strconv.Atoi(part[1:]); err == nil {
						season.SeasonNumber = num
						break
					}
				}
			}
		}

		if season.SeasonNumber == 0 && season.Name != "" {
			parts := strings.Fields(season.Name)
			for i, part := range parts {
				if strings.ToLower(part) == "season" && i+1 < len(parts) {
					if num, err := strconv.Atoi(parts[i+1]); err == nil {
						season.SeasonNumber = num
						break
					}
				}
			}
		}
	}

	return &season, nil
}
View Source
var ExtractTVSeriesMetadata = func(r io.Reader) (*TVSeries, error) {
	doc, err := goquery.NewDocumentFromReader(r)
	if err != nil {
		return nil, err
	}
	var series TVSeries
	found := false
	doc.Find("script[type='application/ld+json']").Each(func(i int, s *goquery.Selection) {
		var tmp map[string]any
		if err := json.Unmarshal([]byte(s.Text()), &tmp); err == nil {
			if t, ok := tmp["@type"].(string); ok && t == "TVSeries" {
				if err := json.Unmarshal([]byte(s.Text()), &series); err == nil {
					found = true
				}
			}
		}
	})
	if !found {
		return nil, fmt.Errorf("no TVSeries JSON-LD found")
	}
	return &series, nil
}
View Source
var FetchHTML = func(url string) (string, error) {
	var html string
	c := colly.NewCollector(
		colly.AllowedDomains("www.rottentomatoes.com", "rottentomatoes.com"),
	)
	c.OnResponse(func(r *colly.Response) { html = string(r.Body) })
	if err := c.Visit(url); err != nil {
		return "", err
	}
	return html, nil
}
View Source
var FetchMovie = func(url string) (*Movie, error) {
	html, err := FetchHTML(url)
	if err != nil {
		return nil, err
	}
	return ExtractMovieMetadata(strings.NewReader(html))
}
View Source
var FetchTVSeason = func(url string) (*TVSeason, error) {
	html, err := FetchHTML(url)
	if err != nil {
		return nil, err
	}
	return ExtractTVSeasonMetadata(strings.NewReader(html))
}
View Source
var FetchTVSeries = func(url string) (*TVSeries, error) {
	html, err := FetchHTML(url)
	if err != nil {
		return nil, err
	}
	return ExtractTVSeriesMetadata(strings.NewReader(html))
}
View Source
var MovieSample []byte

From: https://www.rottentomatoes.com/m/the_fantastic_four_first_steps

View Source
var MovieSearchSample []byte

From: https://www.rottentomatoes.com/search?search=Fantastic%20Four

View Source
var ParseSearch = func(r io.Reader) ([]Media, error) {
	doc, err := goquery.NewDocumentFromReader(r)
	if err != nil {
		return nil, err
	}

	var results []Media
	doc.Find("search-page-result").Each(func(i int, resultBlock *goquery.Selection) {
		mediaType, _ := resultBlock.Attr("type")

		resultBlock.Find("search-page-media-row").Each(func(j int, s *goquery.Selection) {
			link, _ := s.Find("a[slot='thumbnail']").Attr("href")
			if link == "" {
				link, _ = s.Find("a[slot='title']").Attr("href")
				if link == "" {
					return
				}
			}

			title := s.Find("a[slot='title']").Text()

			var itemKind MediaKind
			switch mediaType {
			case "movie":
				itemKind = MovieKind
			case "tvSeries":
				itemKind = TVKind
			default:
				if strings.HasPrefix(link, "/m/") {
					itemKind = MovieKind
				} else if strings.HasPrefix(link, "/tv/") {
					itemKind = TVKind
				}
			}

			score, _ := s.Attr("tomatometerscore")
			if score == "" {
				score = "--"
			}

			certified := false
			if v, ok := s.Attr("tomatometeriscertified"); ok && v == "true" {
				certified = true
			}

			results = append(results, Media{
				Title:          strings.TrimSpace(title),
				Link:           link,
				Type:           itemKind,
				CriticScore:    score,
				CertifiedFresh: certified,
			})
		})
	})

	return results, nil
}

ParseSearch parses Rotten Tomatoes search results HTML into Media entries.

View Source
var SearchRottenTomatoes = func(q string) ([]Media, error) {
	searchURL := "https://www.rottentomatoes.com/search?search=" + url.QueryEscape(q)
	html, err := FetchHTML(searchURL)
	if err != nil {
		return nil, err
	}
	return ParseSearch(strings.NewReader(html))
}

SearchRottenTomatoes fetches live search results for a query.

View Source
var SearchSample []byte

From: https://www.rottentomatoes.com/search?search=peacemaker

View Source
var SeasonSample []byte

From: https://www.rottentomatoes.com/tv/peacemaker_2022/s02

View Source
var SeriesSample []byte

From: https://www.rottentomatoes.com/tv/peacemaker_2022

Functions

func AssertMovieInResults

func AssertMovieInResults(t *testing.T, results []*models.Model, expectedTitle string)

AssertMovieInResults checks if a movie with the given title exists in results

func AssertTVShowInResults

func AssertTVShowInResults(t *testing.T, results []*models.Model, expectedTitle string)

AssertTVShowInResults checks if a TV show with the given title exists in results

func PositiveID

func PositiveID(name string, value int64) error

PositiveID validates that an ID is positive

func RequiredString

func RequiredString(name, value string) error

RequiredString validates that a string field is not empty

func SetupFailureMocks

func SetupFailureMocks(t *testing.T, errorMsg string) func()

SetupFailureMocks configures mocks that return errors

func SetupMediaMocks

func SetupMediaMocks(t *testing.T, config MockConfig) func()

SetupMediaMocks configures mock functions for media services testing

func SetupSuccessfulMovieMocks

func SetupSuccessfulMovieMocks(t *testing.T) func()

SetupSuccessfulMovieMocks configures mocks for successful movie operations

func SetupSuccessfulTVMocks

func SetupSuccessfulTVMocks(t *testing.T) func()

SetupSuccessfulTVMocks configures mocks for successful TV operations

func StringLength

func StringLength(name, value string, min, max int) error

StringLength validates string length constraints

func TestMovieSearch

func TestMovieSearch(t *testing.T, service *MovieService, query string, expectedTitleFragment string)

TestMovieSearch runs a standard movie search test

func TestTVSearch

func TestTVSearch(t *testing.T, service *TVService, query string, expectedTitleFragment string)

TestTVSearch runs a standard TV search test

func ValidDate

func ValidDate(name, value string) error

ValidDate validates that a string can be parsed as a date in supported formats

func ValidEmail

func ValidEmail(name, value string) error

ValidEmail validates that a string is a valid email address

func ValidEnum

func ValidEnum(name, value string, allowedValues []string) error

ValidEnum validates that a value is one of the allowed enum values

func ValidFilePath

func ValidFilePath(name, value string) error

ValidFilePath validates that a string looks like a valid file path

func ValidURL

func ValidURL(name, value string) error

ValidURL validates that a string is a valid URL

Types

type APIService

type APIService interface {
	Get(ctx context.Context, id string) (*models.Model, error)
	Search(ctx context.Context, query string, page, limit int) ([]*models.Model, error)
	Check(ctx context.Context) error
	Close() error
}

APIService defines the contract for API interactions

type ATProtoClient

type ATProtoClient interface {
	Authenticate(ctx context.Context, handle, password string) error
	GetSession() (*Session, error)
	IsAuthenticated() bool
	RestoreSession(session *Session) error
	PullDocuments(ctx context.Context) ([]DocumentWithMeta, error)
	PostDocument(ctx context.Context, doc public.Document, isDraft bool) (*DocumentWithMeta, error)
	PatchDocument(ctx context.Context, rkey string, doc public.Document, isDraft bool) (*DocumentWithMeta, error)
	DeleteDocument(ctx context.Context, rkey string, isDraft bool) error
	UploadBlob(ctx context.Context, data []byte, mimeType string) (public.Blob, error)
	GetDefaultPublication(ctx context.Context) (string, error)
	Close() error
}

ATProtoClient defines the interface for AT Protocol operations

type ATProtoService

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

ATProtoService provides AT Protocol operations for leaflet integration

func NewATProtoService

func NewATProtoService() *ATProtoService

NewATProtoService creates a new AT Protocol service

func (*ATProtoService) Authenticate

func (s *ATProtoService) Authenticate(ctx context.Context, handle, password string) error

Authenticate logs in with BlueSky/AT Protocol credentials

func (*ATProtoService) Close

func (s *ATProtoService) Close() error

Close cleans up resources

func (*ATProtoService) DeleteDocument

func (s *ATProtoService) DeleteDocument(ctx context.Context, rkey string, isDraft bool) error

DeleteDocument removes a document from the user's repository

func (*ATProtoService) GetDefaultPublication

func (s *ATProtoService) GetDefaultPublication(ctx context.Context) (string, error)

GetDefaultPublication returns the URI of the first available publication for the authenticated user

Returns an error if no publications exist

func (*ATProtoService) GetSession

func (s *ATProtoService) GetSession() (*Session, error)

GetSession returns the current session information

func (*ATProtoService) IsAuthenticated

func (s *ATProtoService) IsAuthenticated() bool

IsAuthenticated checks if the service has a valid session

func (*ATProtoService) ListPublications

func (s *ATProtoService) ListPublications(ctx context.Context) ([]PublicationWithMeta, error)

ListPublications fetches available publications for the authenticated user

func (*ATProtoService) PatchDocument

func (s *ATProtoService) PatchDocument(ctx context.Context, rkey string, doc public.Document, isDraft bool) (*DocumentWithMeta, error)

PatchDocument updates an existing document in the user's repository

func (*ATProtoService) PostDocument

func (s *ATProtoService) PostDocument(ctx context.Context, doc public.Document, isDraft bool) (*DocumentWithMeta, error)

PostDocument creates a new document in the user's repository

func (*ATProtoService) PullDocuments

func (s *ATProtoService) PullDocuments(ctx context.Context) ([]DocumentWithMeta, error)

PullDocuments fetches all leaflet documents from the user's repository

func (*ATProtoService) RefreshToken

func (s *ATProtoService) RefreshToken(ctx context.Context) error

RefreshToken refreshes the access token using the refresh token This extends the session without requiring the user to re-authenticate

func (*ATProtoService) RestoreSession

func (s *ATProtoService) RestoreSession(session *Session) error

RestoreSession restores a previously authenticated session from stored credentials and automatically refreshes the token if expired

func (*ATProtoService) UploadBlob

func (s *ATProtoService) UploadBlob(ctx context.Context, data []byte, mimeType string) (public.Blob, error)

UploadBlob uploads binary data as a blob to AT Protocol

type AggregateRating

type AggregateRating struct {
	RatingValue string `json:"ratingValue"`
	RatingCount int    `json:"ratingCount"`
	ReviewCount int    `json:"reviewCount"`
}

type BookService

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

BookService implements APIService for Open Library

func NewBookService

func NewBookService(baseURL string) *BookService

NewBookService creates a new book service with rate limiting

func (*BookService) Check

func (bs *BookService) Check(ctx context.Context) error

Check verifies the API connection

func (*BookService) Close

func (bs *BookService) Close() error

Close cleans up the service resources

HTTP client doesn't need explicit cleanup

func (*BookService) Get

func (bs *BookService) Get(ctx context.Context, id string) (*models.Model, error)

Get retrieves a specific book by Open Library work key

func (*BookService) Search

func (bs *BookService) Search(ctx context.Context, query string, page, limit int) ([]*models.Model, error)

Search searches for books using the Open Library API

type DefaultFetcher

type DefaultFetcher struct{}

DefaultFetcher provides the default implementation using colly

func (*DefaultFetcher) MakeRequest

func (f *DefaultFetcher) MakeRequest(url string) (string, error)

func (*DefaultFetcher) MovieRequest

func (f *DefaultFetcher) MovieRequest(url string) (*Movie, error)

func (*DefaultFetcher) Search

func (f *DefaultFetcher) Search(query string) ([]Media, error)

func (*DefaultFetcher) TVRequest

func (f *DefaultFetcher) TVRequest(url string) (*TVSeries, error)

type DocumentWithMeta

type DocumentWithMeta struct {
	Document public.Document
	Meta     public.DocumentMeta
}

DocumentWithMeta combines a document with its repository metadata

type Fetchable

type Fetchable interface {
	MakeRequest(url string) (string, error)
	MovieRequest(url string) (*Movie, error)
	TVRequest(url string) (*TVSeries, error)
}

type Media

type Media struct {
	Title          string
	Link           string
	Type           MediaKind
	CriticScore    string
	CertifiedFresh bool
}

func GetSampleMovieSearchResults

func GetSampleMovieSearchResults() ([]Media, error)

Sample data access helpers - these use the embedded samples

func GetSampleSearchResults

func GetSampleSearchResults() ([]Media, error)

type MediaKind

type MediaKind string
const (
	TVKind    MediaKind = "tv"
	MovieKind MediaKind = "movie"
)

type MockATProtoService

type MockATProtoService struct {
	AuthenticateFunc          func(ctx context.Context, handle, password string) error
	GetSessionFunc            func() (*Session, error)
	IsAuthenticatedVal        bool
	RestoreSessionFunc        func(session *Session) error
	PullDocumentsFunc         func(ctx context.Context) ([]DocumentWithMeta, error)
	PostDocumentFunc          func(ctx context.Context, doc public.Document, isDraft bool) (*DocumentWithMeta, error)
	PatchDocumentFunc         func(ctx context.Context, rkey string, doc public.Document, isDraft bool) (*DocumentWithMeta, error)
	DeleteDocumentFunc        func(ctx context.Context, rkey string, isDraft bool) error
	UploadBlobFunc            func(ctx context.Context, data []byte, mimeType string) (public.Blob, error)
	GetDefaultPublicationFunc func(ctx context.Context) (string, error)
	CloseFunc                 func() error
	Session                   *Session // Exported for test access
}

MockATProtoService is a mock implementation of ATProtoService for testing

func NewMockATProtoService

func NewMockATProtoService() *MockATProtoService

NewMockATProtoService creates a new mock AT Proto service

func SetupSuccessfulAuthMocks

func SetupSuccessfulAuthMocks() *MockATProtoService

SetupSuccessfulAuthMocks configures mock for successful authentication

func SetupSuccessfulPullMocks

func SetupSuccessfulPullMocks() *MockATProtoService

SetupSuccessfulPullMocks configures mock for successful document pull

func (*MockATProtoService) Authenticate

func (m *MockATProtoService) Authenticate(ctx context.Context, handle, password string) error

Authenticate mocks authentication

func (*MockATProtoService) Close

func (m *MockATProtoService) Close() error

Close mocks cleanup

func (*MockATProtoService) DeleteDocument

func (m *MockATProtoService) DeleteDocument(ctx context.Context, rkey string, isDraft bool) error

DeleteDocument mocks deleting a document

func (*MockATProtoService) GetDefaultPublication

func (m *MockATProtoService) GetDefaultPublication(ctx context.Context) (string, error)

GetDefaultPublication mocks getting the default publication

func (*MockATProtoService) GetSession

func (m *MockATProtoService) GetSession() (*Session, error)

GetSession returns the current session

func (*MockATProtoService) IsAuthenticated

func (m *MockATProtoService) IsAuthenticated() bool

IsAuthenticated returns authentication status

func (*MockATProtoService) PatchDocument

func (m *MockATProtoService) PatchDocument(ctx context.Context, rkey string, doc public.Document, isDraft bool) (*DocumentWithMeta, error)

PatchDocument mocks patching a document

func (*MockATProtoService) PostDocument

func (m *MockATProtoService) PostDocument(ctx context.Context, doc public.Document, isDraft bool) (*DocumentWithMeta, error)

PostDocument mocks posting a document

func (*MockATProtoService) PullDocuments

func (m *MockATProtoService) PullDocuments(ctx context.Context) ([]DocumentWithMeta, error)

PullDocuments mocks pulling documents

func (*MockATProtoService) RestoreSession

func (m *MockATProtoService) RestoreSession(session *Session) error

RestoreSession restores a session

func (*MockATProtoService) UploadBlob

func (m *MockATProtoService) UploadBlob(ctx context.Context, data []byte, mimeType string) (public.Blob, error)

UploadBlob mocks blob upload

type MockConfig

type MockConfig struct {
	SearchResults  []Media
	SearchError    error
	MovieResult    *Movie
	MovieError     error
	TVSeriesResult *TVSeries
	TVSeriesError  error
	TVSeasonResult *TVSeason
	TVSeasonError  error
	HTMLResult     string
	HTMLError      error
}

MockConfig holds configuration for mocking media services

type MockSetup

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

MockSetup contains the original function variables for restoration

type Movie

type Movie struct {
	Context         string          `json:"@context"`
	Type            string          `json:"@type"`
	Name            string          `json:"name"`
	URL             string          `json:"url"`
	Description     string          `json:"description"`
	Image           string          `json:"image"`
	Genre           []string        `json:"genre"`
	ContentRating   string          `json:"contentRating"`
	DateCreated     string          `json:"dateCreated"`
	Actors          []Person        `json:"actor"`
	Directors       []Person        `json:"director"`
	Producers       []Person        `json:"producer"`
	AggregateRating AggregateRating `json:"aggregateRating"`
}

func GetSampleMovie

func GetSampleMovie() (*Movie, error)

type MovieService

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

func CreateMovieService

func CreateMovieService() *MovieService

CreateMovieService returns a new movie service for testing

func NewMovieService

func NewMovieService() *MovieService

NewMovieService creates a new movie service with rate limiting

func NewMovieSrvWithOpts

func NewMovieSrvWithOpts(baseURL string, fetcher Fetchable, searcher Searchable) *MovieService

NewMovieSrvWithOpts creates a new movie service with custom dependencies (for testing)

func (*MovieService) Check

func (s *MovieService) Check(ctx context.Context) error

Check verifies the API connection to Rotten Tomatoes

func (*MovieService) Close

func (s *MovieService) Close() error

Close cleans up the service resources

func (*MovieService) Get

func (s *MovieService) Get(ctx context.Context, id string) (*models.Model, error)

Get retrieves a specific movie by its Rotten Tomatoes URL

func (*MovieService) Search

func (s *MovieService) Search(ctx context.Context, query string, page, limit int) ([]*models.Model, error)

Search searches for movies on Rotten Tomatoes

type MutateRecordOutput

type MutateRecordOutput struct {
	Cid string `json:"cid"`
	Uri string `json:"uri"`
}

type OpenLibraryAuthorKey

type OpenLibraryAuthorKey struct {
	Key string `json:"key"`
}

OpenLibraryAuthorKey represents an author key

type OpenLibraryAuthorRef

type OpenLibraryAuthorRef struct {
	Author OpenLibraryAuthorKey `json:"author"`
	Type   OpenLibraryType      `json:"type"`
}

OpenLibraryAuthorRef represents an author reference in a work

type OpenLibrarySearchDoc

type OpenLibrarySearchDoc struct {
	Key              string   `json:"key"`
	Title            string   `json:"title"`
	AuthorName       []string `json:"author_name"`
	FirstPublishYear int      `json:"first_publish_year"`
	PublishYear      []int    `json:"publish_year"`
	Edition_count    int      `json:"edition_count"`
	ISBN             []string `json:"isbn"`
	PublisherName    []string `json:"publisher"`
	Subject          []string `json:"subject"`
	CoverI           int      `json:"cover_i"`
	HasFulltext      bool     `json:"has_fulltext"`
	PublicScanB      bool     `json:"public_scan_b"`
	ReadinglogCount  int      `json:"readinglog_count"`
	WantToReadCount  int      `json:"want_to_read_count"`
	CurrentlyReading int      `json:"currently_reading_count"`
	AlreadyReadCount int      `json:"already_read_count"`
}

OpenLibrarySearchDoc represents a book document in search results

type OpenLibrarySearchResponse

type OpenLibrarySearchResponse struct {
	NumFound      int                    `json:"numFound"`
	Start         int                    `json:"start"`
	NumFoundExact bool                   `json:"numFoundExact"`
	Docs          []OpenLibrarySearchDoc `json:"docs"`
}

OpenLibrarySearchResponse represents the search response from Open Library

type OpenLibraryType

type OpenLibraryType struct {
	Key string `json:"key"`
}

OpenLibraryType represents a type reference

type OpenLibraryWork

type OpenLibraryWork struct {
	Key              string                 `json:"key"`
	Title            string                 `json:"title"`
	Authors          []OpenLibraryAuthorRef `json:"authors"`
	Description      any                    `json:"description"` // Can be string or object
	Subjects         []string               `json:"subjects"`
	Covers           []int                  `json:"covers"`
	FirstPublishDate string                 `json:"first_publish_date"`
}

OpenLibraryWork represents a work details from Open Library

type PartOfSeries

type PartOfSeries struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

type Person

type Person struct {
	Name   string `json:"name"`
	SameAs string `json:"sameAs"`
	Image  string `json:"image"`
}

type PublicationWithMeta

type PublicationWithMeta struct {
	Publication public.Publication
	RKey        string
	CID         string
	URI         string
}

PublicationWithMeta combines a publication with its metadata

type Searchable

type Searchable interface {
	Search(query string) ([]Media, error)
}

type Season

type Season struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

type Session

type Session struct {
	DID           string    // Decentralized Identifier
	Handle        string    // User handle (e.g., username.bsky.social)
	AccessJWT     string    // Access token
	RefreshJWT    string    // Refresh token
	PDSURL        string    // Personal Data Server URL
	ExpiresAt     time.Time // When access token expires
	Authenticated bool      // Whether session is valid
}

Session holds authentication session information

type TVSeason

type TVSeason struct {
	Context         string          `json:"@context"`
	Type            string          `json:"@type"`
	Name            string          `json:"name"`
	URL             string          `json:"url"`
	Description     string          `json:"description"`
	Image           string          `json:"image"`
	SeasonNumber    int             `json:"seasonNumber"`
	DatePublished   string          `json:"datePublished"`
	PartOfSeries    PartOfSeries    `json:"partOfSeries"`
	AggregateRating AggregateRating `json:"aggregateRating"`
}

func GetSampleTVSeason

func GetSampleTVSeason() (*TVSeason, error)

type TVSeries

type TVSeries struct {
	Context         string          `json:"@context"`
	Type            string          `json:"@type"`
	Name            string          `json:"name"`
	URL             string          `json:"url"`
	Description     string          `json:"description"`
	Image           string          `json:"image"`
	Genre           []string        `json:"genre"`
	ContentRating   string          `json:"contentRating"`
	DateCreated     string          `json:"dateCreated"`
	NumberOfSeasons int             `json:"numberOfSeasons"`
	Actors          []Person        `json:"actor"`
	Producers       []Person        `json:"producer"`
	AggregateRating AggregateRating `json:"aggregateRating"`
	Seasons         []Season        `json:"containsSeason"`
}

func GetSampleTVSeries

func GetSampleTVSeries() (*TVSeries, error)

type TVService

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

func CreateTVService

func CreateTVService() *TVService

CreateTVService returns a new TV service for testing

func NewTVService

func NewTVService() *TVService

NewTVService creates a new TV service with rate limiting

func NewTVServiceWithDeps

func NewTVServiceWithDeps(baseURL string, fetcher Fetchable, searcher Searchable) *TVService

NewTVServiceWithDeps creates a new TV service with custom dependencies (for testing)

func (*TVService) Check

func (s *TVService) Check(ctx context.Context) error

Check verifies the API connection to Rotten Tomatoes

func (*TVService) Close

func (s *TVService) Close() error

Close cleans up the service resources

func (*TVService) Get

func (s *TVService) Get(ctx context.Context, id string) (*models.Model, error)

Get retrieves a specific TV show by its Rotten Tomatoes URL

func (*TVService) Search

func (s *TVService) Search(ctx context.Context, query string, page, limit int) ([]*models.Model, error)

Search searches for TV shows on Rotten Tomatoes

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError represents a validation error

func NewValidationError

func NewValidationError(f, m string) ValidationError

func (ValidationError) Error

func (e ValidationError) Error() string

type ValidationErrors

type ValidationErrors []ValidationError

ValidationErrors represents multiple validation errors

func (ValidationErrors) Error

func (e ValidationErrors) Error() string

type Validator

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

Validator provides a fluent interface for validation

func NewValidator

func NewValidator() *Validator

NewValidator creates a new validator instance

func (*Validator) Check

func (v *Validator) Check(err error) *Validator

Check adds a validation check

func (*Validator) Errors

func (v *Validator) Errors() error

Errors returns all validation errors

func (*Validator) IsValid

func (v *Validator) IsValid() bool

IsValid returns true if no validation errors occurred

Jump to

Keyboard shortcuts

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