package db import ( "context" "database/sql" "errors" "time" ) type Module struct { Id int64 TextId string Title string Description string Url string Version string Availability int CreatedAt time.Time Languages []Language Compatibility Compatibility } type Language struct { Id int64 Lang string Name string Path string } type Modules []Module func (modules Modules) GetById(id int) *Module { return &modules[id] } func (m FoundryStateModel) InsertModule(module *Module, stateId int64) error { query := ` INSERT INTO modules (state_id, text_id, title, description, url, version, availability) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at` args := []any{stateId, module.TextId, module.Title, module.Description, module.Url, module.Version, module.Availability} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := m.DB.QueryRowContext(ctx, query, args...).Scan(&module.Id, &module.CreatedAt) if err != nil { return err } for i := range module.Languages { err = m.InsertModuleLanguages(&module.Languages[i], module.Id) if err != nil && err != sql.ErrNoRows { return err } } return m.InsertModuleCompatibility(&module.Compatibility, module.Id) } func (m FoundryStateModel) InsertModuleCompatibility(compatibility *Compatibility, moduleId int64) error { query := ` INSERT INTO modules_compatibility (module_id, minimum, verified, maximum) VALUES ($1, $2, $3, $4) RETURNING id` args := []any{moduleId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id) if err != nil && err != sql.ErrNoRows { return err } return nil } func (m FoundryStateModel) InsertModuleLanguages(lang *Language, moduleId int64) error { query := ` INSERT INTO modules_languages (module_id, language, name, path) VALUES ($1, $2, $3, $4) RETURNING id` args := []any{moduleId, lang.Lang, lang.Name, lang.Path} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := m.DB.QueryRowContext(ctx, query, args...).Scan(&lang.Id) if err != nil && err != sql.ErrNoRows { return err } return nil } func (m FoundryStateModel) GetModules(idState int64) (Modules, error) { if idState < 1 { return nil, ErrorRecordNotFound } query := ` SELECT id, created_at, text_id, title, description, url, version, availability FROM modules WHERE state_id = $1` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() rows, err := m.DB.QueryContext(ctx, query, idState) if err != nil { switch { case errors.Is(err, sql.ErrNoRows): return nil, ErrorRecordNotFound default: return nil, err } } modules := make(Modules, 0, 1) for rows.Next() { var module Module err := rows.Scan( &module.Id, &module.CreatedAt, &module.TextId, &module.Title, &module.Description, &module.Url, &module.Version, &module.Availability, ) if err != nil { return nil, err } module.Languages, err = m.GetModuleLanguages(module.Id) if err != nil { return nil, err } compatibility, err := m.GetModuleCompatibility(module.Id) if err != nil { return nil, err } module.Compatibility = *compatibility modules = append(modules, module) } if err := rows.Err(); err != nil { return nil, err } return modules, nil } func (m FoundryStateModel) GetModuleCompatibility(idModule int64) (*Compatibility, error) { if idModule < 1 { return nil, ErrorRecordNotFound } query := ` SELECT id, minimum, verified, maximum FROM modules_compatibility WHERE module_id = $1` var compatibility Compatibility ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := m.DB.QueryRowContext(ctx, query, idModule).Scan( &compatibility.Id, &compatibility.Minimum, &compatibility.Verified, &compatibility.Maximum, ) if err != nil { switch { case errors.Is(err, sql.ErrNoRows): return nil, ErrorRecordNotFound default: return nil, err } } return &compatibility, nil } func (m FoundryStateModel) GetModuleLanguages(idModule int64) ([]Language, error) { if idModule < 1 { return nil, ErrorRecordNotFound } query := ` SELECT id, language, name, path FROM modules_languages WHERE module_id = $1` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() rows, err := m.DB.QueryContext(ctx, query, idModule) if err != nil { switch { case errors.Is(err, sql.ErrNoRows): return nil, ErrorRecordNotFound default: return nil, err } } languages := make([]Language, 0, 1) for rows.Next() { var lang Language err := rows.Scan( &lang.Id, &lang.Lang, &lang.Name, &lang.Path, ) if err != nil { return nil, err } languages = append(languages, lang) } if err := rows.Err(); err != nil { return nil, err } return languages, nil } func (m FoundryStateModel) DeleteModules(idState int64) error { if idState < 1 { return ErrorRecordNotFound } query := ` DELETE FROM modules WHERE state_id = $1` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() result, err := m.DB.ExecContext(ctx, query, idState) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrorRecordNotFound } return nil } func (m FoundryStateModel) DeleteModule(idModule int64) error { if idModule < 1 { return ErrorRecordNotFound } query := ` DELETE FROM modules WHERE id = $1` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() result, err := m.DB.ExecContext(ctx, query, idModule) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrorRecordNotFound } return nil }