diff --git a/pkg/models/category.go b/pkg/models/category.go new file mode 100644 index 0000000..95206e3 --- /dev/null +++ b/pkg/models/category.go @@ -0,0 +1,32 @@ +package models + +// CategoryModel represents the category data stored in the database. +type CategoryModel struct { + ModelLifecycles `json:"-"` + ID string `db:"id" json:"id"` + Name string `db:"name" json:"name"` +} + +func (m *CategoryModel) BeforeCreate() error { + return nil +} + +func (m *CategoryModel) AfterCreate() error { + return nil +} + +func (m *CategoryModel) BeforeUpdate() error { + return nil +} + +func (m *CategoryModel) AfterUpdate() error { + return nil +} + +func (m *CategoryModel) BeforeDelete() error { + return nil +} + +func (m *CategoryModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/event.go b/pkg/models/event.go new file mode 100644 index 0000000..75f6c3b --- /dev/null +++ b/pkg/models/event.go @@ -0,0 +1,41 @@ +package models + +import ( + "database/sql" + "time" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/types" +) + +// EventModel represents the event data stored in the database. +type EventModel struct { + Model + Name string `db:"name" json:"name"` + OrganizerId string `db:"organizer_id" json:"organizer_id"` + Description sql.NullString `db:"description" json:"description"` + StartDate time.Time `db:"start_date" json:"start_date"` + EndDate time.Time `db:"end_date" json:"end_date"` + IsPaid bool `db:"is_paid" json:"is_paid"` + EventType types.EventType `db:"event_type" json:"event_type"` + Country sql.NullString `db:"country" json:"country"` + City sql.NullString `db:"city" json:"city"` + Slug string `db:"slug" json:"slug"` + Likes int64 `db:"likes" json:"likes"` + Follows int64 `db:"follows" json:"follows"` + Attendees int64 `db:"attendees" json:"attendees"` +} + +// BeforeCreate overrides model lifecycle hook +func (m *EventModel) BeforeCreate() error { + m.Likes = 0 + m.Follows = 0 + m.Attendees = 0 + // TODO: generate value for slug + return nil +} + +// BeforeUpdate overrides model lifecycle hook, updating the updated_at time. +func (m *EventModel) BeforeUpdate() error { + m.UpdatedAt = time.Now() + return nil +} diff --git a/pkg/models/event_attendee.go b/pkg/models/event_attendee.go new file mode 100644 index 0000000..0e46c1b --- /dev/null +++ b/pkg/models/event_attendee.go @@ -0,0 +1,35 @@ +package models + +import "time" + +// EventAttendeeModel represents the event_attendee data stored in the database. +type EventAttendeeModel struct { + ModelLifecycles `json:"-"` + EventId string `db:"event_id" json:"event_id"` + AttendeeId string `db:"attendee_id" json:"attendee_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +func (m *EventAttendeeModel) BeforeCreate() error { + return nil +} + +func (m *EventAttendeeModel) AfterCreate() error { + return nil +} + +func (m *EventAttendeeModel) BeforeUpdate() error { + return nil +} + +func (m *EventAttendeeModel) AfterUpdate() error { + return nil +} + +func (m *EventAttendeeModel) BeforeDelete() error { + return nil +} + +func (m *EventAttendeeModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/event_category.go b/pkg/models/event_category.go new file mode 100644 index 0000000..43da2ca --- /dev/null +++ b/pkg/models/event_category.go @@ -0,0 +1,32 @@ +package models + +// EventCategoryModel represents the event_category data stored in the database. +type EventCategoryModel struct { + ModelLifecycles `json:"-"` + EventId string `db:"event_id" json:"event_id"` + CategoryId string `db:"category_id" json:"category_id"` +} + +func (m *EventCategoryModel) BeforeCreate() error { + return nil +} + +func (m *EventCategoryModel) AfterCreate() error { + return nil +} + +func (m *EventCategoryModel) BeforeUpdate() error { + return nil +} + +func (m *EventCategoryModel) AfterUpdate() error { + return nil +} + +func (m *EventCategoryModel) BeforeDelete() error { + return nil +} + +func (m *EventCategoryModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/event_follower.go b/pkg/models/event_follower.go new file mode 100644 index 0000000..7649804 --- /dev/null +++ b/pkg/models/event_follower.go @@ -0,0 +1,32 @@ +package models + +// EventFollowerModel represents the event_follower data stored in the database. +type EventFollowerModel struct { + ModelLifecycles `json:"-"` + EventId string `db:"event_id" json:"event_id"` + FollowerId string `db:"follower_id" json:"follower_id"` +} + +func (m *EventFollowerModel) BeforeCreate() error { + return nil +} + +func (m *EventFollowerModel) AfterCreate() error { + return nil +} + +func (m *EventFollowerModel) BeforeUpdate() error { + return nil +} + +func (m *EventFollowerModel) AfterUpdate() error { + return nil +} + +func (m *EventFollowerModel) BeforeDelete() error { + return nil +} + +func (m *EventFollowerModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/event_like.go b/pkg/models/event_like.go new file mode 100644 index 0000000..16e6bda --- /dev/null +++ b/pkg/models/event_like.go @@ -0,0 +1,32 @@ +package models + +// EventLikeModel represents the event_like data stored in the database. +type EventLikeModel struct { + ModelLifecycles `json:"-"` + EventId string `db:"event_id" json:"event_id"` + UserId string `db:"user_id" json:"user_id"` +} + +func (m *EventLikeModel) BeforeCreate() error { + return nil +} + +func (m *EventLikeModel) AfterCreate() error { + return nil +} + +func (m *EventLikeModel) BeforeUpdate() error { + return nil +} + +func (m *EventLikeModel) AfterUpdate() error { + return nil +} + +func (m *EventLikeModel) BeforeDelete() error { + return nil +} + +func (m *EventLikeModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/event_tag.go b/pkg/models/event_tag.go new file mode 100644 index 0000000..9ae9ae3 --- /dev/null +++ b/pkg/models/event_tag.go @@ -0,0 +1,32 @@ +package models + +// EventTagModel represents the event_tag data stored in the database. +type EventTagModel struct { + ModelLifecycles `json:"-"` + EventId string `db:"event_id" json:"event_id"` + TagId string `db:"tag_id" json:"tag_id"` +} + +func (m *EventTagModel) BeforeCreate() error { + return nil +} + +func (m *EventTagModel) AfterCreate() error { + return nil +} + +func (m *EventTagModel) BeforeUpdate() error { + return nil +} + +func (m *EventTagModel) AfterUpdate() error { + return nil +} + +func (m *EventTagModel) BeforeDelete() error { + return nil +} + +func (m *EventTagModel) AfterDelete() error { + return nil +} diff --git a/pkg/models/review.go b/pkg/models/review.go new file mode 100644 index 0000000..717036f --- /dev/null +++ b/pkg/models/review.go @@ -0,0 +1,18 @@ +package models + +import "time" + +// ReviewModel represents the review data stored in the database. +type ReviewModel struct { + Model + Title string `db:"title" json:"title"` + EventId string `db:"event_id" json:"event_id"` + AuthorId string `db:"author_id" json:"author_id"` + Body string `db:"body" json:"body"` +} + +// BeforeUpdate overrides model lifecycle hook, updating the updated_at time. +func (m *ReviewModel) BeforeUpdate() error { + m.UpdatedAt = time.Now() + return nil +} diff --git a/pkg/models/tag.go b/pkg/models/tag.go new file mode 100644 index 0000000..1c25702 --- /dev/null +++ b/pkg/models/tag.go @@ -0,0 +1,32 @@ +package models + +// TagModel represents the tag data stored in the database. +type TagModel struct { + ModelLifecycles `json:"-"` + ID string `db:"id" json:"id"` + Name string `db:"name" json:"name"` +} + +func (m *TagModel) BeforeCreate() error { + return nil +} + +func (m *TagModel) AfterCreate() error { + return nil +} + +func (m *TagModel) BeforeUpdate() error { + return nil +} + +func (m *TagModel) AfterUpdate() error { + return nil +} + +func (m *TagModel) BeforeDelete() error { + return nil +} + +func (m *TagModel) AfterDelete() error { + return nil +} diff --git a/pkg/repository/category.go b/pkg/repository/category.go new file mode 100644 index 0000000..ae35637 --- /dev/null +++ b/pkg/repository/category.go @@ -0,0 +1,118 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// CategoryRepository represents the interface for category-related database operations. +type CategoryRepository interface { + CreateCategory(category *models.CategoryModel) error + GetCategoryByID(id string) (*models.CategoryModel, error) + UpdateCategory(category *models.CategoryModel) error + DeleteCategory(id string) error +} + +type sqlCategoryRepository struct { + database *sql.DB +} + +// NewSQLCategoryRepository creates and returns a new sql flavoured CategoryRepository instance. +func NewSQLCategoryRepository(database *sql.DB) CategoryRepository { + return &sqlCategoryRepository{database: database} +} + +// inserts a new category into the database +func (r *sqlCategoryRepository) CreateCategory(category *models.CategoryModel) error { + category.BeforeCreate() + + query := `INSERT INTO public.categories (name) VALUES ($1) RETURNING id` + + err := r.database.QueryRow(query, category.Name).Scan(&category.ID) + if err != nil { + return fmt.Errorf("failed to create category: %w", err) + } + + category.AfterCreate() + return nil +} + +// retrieves a category from the database by its unique ID, if it exists +func (r *sqlCategoryRepository) GetCategoryByID(id string) (*models.CategoryModel, error) { + query := `SELECT + id, + name + + FROM public.categories WHERE id = $1` + + category := &models.CategoryModel{} + err := r.database.QueryRow(query, id).Scan( + &category.ID, + &category.Name, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrCategoryNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidCategoryId + } + + return category, nil +} + +// updates a category in database +func (r *sqlCategoryRepository) UpdateCategory(category *models.CategoryModel) error { + category.BeforeUpdate() + + query := `UPDATE public.categories SET name = $1 WHERE id = $2` + rs, err := r.database.Exec( + query, + category.Name, + category.ID, + ) + if err != nil { + return err + } + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrCategoryNotFound + } + + category.AfterUpdate() + return nil +} + +// deletes a category from the database. +func (r *sqlCategoryRepository) DeleteCategory(id string) error { + query := `DELETE FROM public.categories WHERE id = $1` + + rs, err := r.database.Exec(query, id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrCategoryNotFound + } + + return nil +} + +// errors +var ( + ErrCategoryNotFound = errors.New("category not found") + ErrInvalidCategoryId = errors.New("invalid category id") +) diff --git a/pkg/repository/event.go b/pkg/repository/event.go new file mode 100644 index 0000000..9049d9b --- /dev/null +++ b/pkg/repository/event.go @@ -0,0 +1,244 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventRepository represents the interface for event-related database operations. +type EventRepository interface { + CreateEvent(event *models.EventModel) error + GetEventByID(id string) (*models.EventModel, error) + GetEventsByOrganizerId(org_id string) ([]models.EventModel, error) + UpdateEvent(event *models.EventModel) error + DeleteEvent(id string) error +} + +type sqlEventRepository struct { + database *sql.DB +} + +// NewSQLEventRepository creates and returns a new sql flavoured EventRepository instance. +func NewSQLEventRepository(database *sql.DB) EventRepository { + return &sqlEventRepository{database: database} +} + +// inserts a new event into the database +func (r *sqlEventRepository) CreateEvent(event *models.EventModel) error { + event.BeforeCreate() + + query := `INSERT INTO public.events (name, organizer_id, description, start_date, end_date, is_paid, event_type, country, city, slug, likes, follows, attendees) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id` + + err := r.database.QueryRow(query, event.Name, event.OrganizerId, event.Description, event.StartDate, event.EndDate, event.IsPaid, event.EventType, event.Country, event.City, event.Slug, event.Likes, event.Follows, event.Attendees).Scan(&event.ID) + if err != nil { + return fmt.Errorf("failed to create event: %w", err) + } + + event.AfterCreate() + return nil +} + +// retrieves an event from the database by its unique ID, if it exists +func (r *sqlEventRepository) GetEventByID(id string) (*models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events WHERE id = $1` + + event := &models.EventModel{} + err := r.database.QueryRow(query, id).Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventId + } + + return event, nil +} + +// retrieves event(s) from database that match with organizer_id +func (r *sqlEventRepository) GetEventsByOrganizerId(org_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events WHERE organizer_id = $1` + + rows, err := r.database.Query(query, org_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// updates an event in database +func (r *sqlEventRepository) UpdateEvent(event *models.EventModel) error { + event.BeforeUpdate() + + query := `UPDATE public.events SET + name = $1, + organizer_id = $2, + description = $3, + start_date = $4, + end_date = $5, + is_paid = $6, + event_type = $7, + country = $8, + city = $9, + slug = $10, + likes = $11, + follows = $12, + attendees = $13, + updated_at = $14 + + WHERE id = $15` + + // prevents a partial event from being submitted + if event.CreatedAt.Unix() == 0 { + return fmt.Errorf("unable to update an event that was not loaded from the database") + } + + rs, err := r.database.Exec( + query, + event.Name, + event.OrganizerId, + event.Description, + event.StartDate, + event.EndDate, + event.IsPaid, + event.EventType, + event.Country, + event.City, + event.Slug, + event.Likes, + event.Follows, + event.Attendees, + event.UpdatedAt, + event.ID, + ) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventNotFound + } + + event.AfterUpdate() + return nil +} + +// deletes an event from the database. +func (r *sqlEventRepository) DeleteEvent(id string) error { + query := `DELETE FROM public.events WHERE id = $1` + + rs, err := r.database.Exec(query, id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventNotFound + } + + return nil +} + +// errors +var ( + ErrEventNotFound = errors.New("event not found") + ErrInvalidEventId = errors.New("invalid event id") +) diff --git a/pkg/repository/event_attendee.go b/pkg/repository/event_attendee.go new file mode 100644 index 0000000..d9934b4 --- /dev/null +++ b/pkg/repository/event_attendee.go @@ -0,0 +1,223 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventAttendeeRepository represents the interface for event_attendee-related database operations. +type EventAttendeeRepository interface { + CreateEventAttendee(obj *models.EventAttendeeModel) error + GetEventAttendee(event_id string, attendee_id string) (*models.EventAttendeeModel, error) + GetAttendeesByEventId(event_id string) ([]models.UserModel, error) + GetEventsByAttendeeId(attendee_id string) ([]models.EventModel, error) + DeleteEventAttendee(event_id string, attendee_id string) error +} + +type sqlEventAttendeeRepository struct { + database *sql.DB +} + +func NewSQLEventAttendeeRepository(database *sql.DB) EventAttendeeRepository { + return &sqlEventAttendeeRepository{database: database} +} + +// inserts a new event_attendee record into the database +func (r *sqlEventAttendeeRepository) CreateEventAttendee(obj *models.EventAttendeeModel) error { + obj.BeforeCreate() + + query := `INSERT INTO public.event_attendees (event_id, attendee_id) + VALUES ($1, $2)` + + _, err := r.database.Exec(query, obj.EventId, obj.AttendeeId) + if err != nil { + return fmt.Errorf("failed to create event_attendee: %w", err) + } + + obj.AfterCreate() + return nil +} + +// retrieves an event_attendee record from the database using event_id and attendee_id, if it exists +func (r *sqlEventAttendeeRepository) GetEventAttendee(event_id string, attendee_id string) (*models.EventAttendeeModel, error) { + query := `SELECT + event_id, + attendee_id, + created_at + + FROM public.event_attendees WHERE event_id = $1 AND attendee_id = $2` + + obj := &models.EventAttendeeModel{} + err := r.database.QueryRow(query, event_id, attendee_id).Scan( + &obj.EventId, + &obj.AttendeeId, + &obj.CreatedAt, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventAttendeeNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventAttendeeId + } + + return obj, nil +} + +// retrieves users from database that are attendees of the given event +func (r *sqlEventAttendeeRepository) GetAttendeesByEventId(event_id string) ([]models.UserModel, error) { + query := `SELECT + id, + username, + email, + password, + first_name, + last_name, + birth_date, + role, + verified, + about, + created_at, + updated_at, + google_id, + avatar_url + + FROM public.users + WHERE id IN ( + SELECT attendee_id + FROM public.event_attendees + WHERE event_id = $1 + )` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var attendees []models.UserModel + for rows.Next() { + var attendee models.UserModel + if err := rows.Scan( + &attendee.ID, + &attendee.Username, + &attendee.Email, + &attendee.Password, + &attendee.FirstName, + &attendee.LastName, + &attendee.BirthDate, + &attendee.Role, + &attendee.Verified, + &attendee.About, + &attendee.CreatedAt, + &attendee.UpdatedAt, + &attendee.GoogleId, + &attendee.AvatarUrl, + ); err != nil { + return nil, err + } + attendees = append(attendees, attendee) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return attendees, nil +} + +// retrieves events from database that match with attendee_id +func (r *sqlEventAttendeeRepository) GetEventsByAttendeeId(attendee_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events + WHERE id IN ( + SELECT event_id + FROM public.event_attendees + WHERE attendee_id = $1 + )` + + rows, err := r.database.Query(query, attendee_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// deletes an event_attendee record from the database. +func (r *sqlEventAttendeeRepository) DeleteEventAttendee(event_id string, attendee_id string) error { + query := `DELETE FROM public.event_attendees WHERE event_id = $1 AND attendee_id = $2` + + rs, err := r.database.Exec(query, event_id, attendee_id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventAttendeeNotFound + } + + return nil +} + +// errors +var ( + ErrEventAttendeeNotFound = errors.New("event_attendee not found") + ErrInvalidEventAttendeeId = errors.New("invalid event_attendee id") +) diff --git a/pkg/repository/event_category.go b/pkg/repository/event_category.go new file mode 100644 index 0000000..a9d3350 --- /dev/null +++ b/pkg/repository/event_category.go @@ -0,0 +1,197 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventCategoryRepository represents the interface for event_category-related database operations. +type EventCategoryRepository interface { + CreateEventCategory(obj *models.EventCategoryModel) error + GetEventCategory(event_id string, category_id string) (*models.EventCategoryModel, error) + GetCategoriesByEventId(event_id string) ([]models.CategoryModel, error) + GetEventsByCategoryId(category_id string) ([]models.EventModel, error) + DeleteEventCategory(event_id string, category_id string) error +} + +type sqlEventCategoryRepository struct { + database *sql.DB +} + +func NewSQLEventCategoryRepository(database *sql.DB) EventCategoryRepository { + return &sqlEventCategoryRepository{database: database} +} + +// inserts a new event_category into the database +func (r *sqlEventCategoryRepository) CreateEventCategory(obj *models.EventCategoryModel) error { + obj.BeforeCreate() + + query := `INSERT INTO public.event_categories (event_id, category_id) + VALUES ($1, $2)` + + _, err := r.database.Exec(query, obj.EventId, obj.CategoryId) + if err != nil { + return fmt.Errorf("failed to create event_category: %w", err) + } + + obj.AfterCreate() + return nil +} + +// retrieves an event_category from the database using its event_id and category_id, if it exists +func (r *sqlEventCategoryRepository) GetEventCategory(event_id string, category_id string) (*models.EventCategoryModel, error) { + query := `SELECT + event_id, + category_id + + FROM public.event_categories WHERE event_id = $1 AND category_id = $2` + + obj := &models.EventCategoryModel{} + err := r.database.QueryRow(query, event_id, category_id).Scan( + &obj.EventId, + &obj.CategoryId, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventCategoryNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventCategoryId + } + + return obj, nil +} + +// retrieves categories from the database that are associated with the given event +func (r *sqlEventCategoryRepository) GetCategoriesByEventId(event_id string) ([]models.CategoryModel, error) { + query := `SELECT + id, + name + + FROM public.categories + WHERE id IN ( + SELECT category_id + FROM public.event_categories + WHERE event_id = $1 + )` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var categories []models.CategoryModel + for rows.Next() { + var category models.CategoryModel + if err := rows.Scan( + &category.ID, + &category.Name, + ); err != nil { + return nil, err + } + categories = append(categories, category) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return categories, nil +} + +// retrieves events from database that match with category_id +func (r *sqlEventCategoryRepository) GetEventsByCategoryId(category_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events + WHERE id IN ( + SELECT event_id + FROM public.event_categories + WHERE category_id = $1 + )` + + rows, err := r.database.Query(query, category_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// deletes an event_category from the database. +func (r *sqlEventCategoryRepository) DeleteEventCategory(event_id string, category_id string) error { + query := `DELETE FROM public.event_categories WHERE event_id = $1 AND category_id = $2` + + rs, err := r.database.Exec(query, event_id, category_id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventCategoryNotFound + } + + return nil +} + +// errors +var ( + ErrEventCategoryNotFound = errors.New("event_category not found") + ErrInvalidEventCategoryId = errors.New("invalid event_category id") +) diff --git a/pkg/repository/event_follower.go b/pkg/repository/event_follower.go new file mode 100644 index 0000000..02c37db --- /dev/null +++ b/pkg/repository/event_follower.go @@ -0,0 +1,221 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventFollowerRepository represents the interface for event_follower-related database operations. +type EventFollowerRepository interface { + CreateEventFollower(obj *models.EventFollowerModel) error + GetEventFollower(event_id string, follower_id string) (*models.EventFollowerModel, error) + GetFollowersByEventId(event_id string) ([]models.UserModel, error) + GetEventsByFollowerId(follower_id string) ([]models.EventModel, error) + DeleteEventFollower(event_id string, follower_id string) error +} + +type sqlEventFollowerRepository struct { + database *sql.DB +} + +func NewSQLEventFollowerRepository(database *sql.DB) EventFollowerRepository { + return &sqlEventFollowerRepository{database: database} +} + +// inserts a new event_follower record into the database +func (r *sqlEventFollowerRepository) CreateEventFollower(obj *models.EventFollowerModel) error { + obj.BeforeCreate() + + query := `INSERT INTO public.event_followers (event_id, follower_id) + VALUES ($1, $2)` + + _, err := r.database.Exec(query, obj.EventId, obj.FollowerId) + if err != nil { + return fmt.Errorf("failed to create event_follower: %w", err) + } + + obj.AfterCreate() + return nil +} + +// retrieves an event_follower record from the database using its event_id and follower_id, if it exists +func (r *sqlEventFollowerRepository) GetEventFollower(event_id string, follower_id string) (*models.EventFollowerModel, error) { + query := `SELECT + event_id, + follower_id + + FROM public.event_followers WHERE event_id = $1 AND follower_id = $2` + + obj := &models.EventFollowerModel{} + err := r.database.QueryRow(query, event_id, follower_id).Scan( + &obj.EventId, + &obj.FollowerId, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventFollowerNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventFollowerId + } + + return obj, nil +} + +// retrieves users from database that are followers of the given event +func (r *sqlEventFollowerRepository) GetFollowersByEventId(event_id string) ([]models.UserModel, error) { + query := `SELECT + id, + username, + email, + password, + first_name, + last_name, + birth_date, + role, + verified, + about, + created_at, + updated_at, + google_id, + avatar_url + + FROM public.users + WHERE id IN ( + SELECT follower_id + FROM public.event_followers + WHERE event_id = $1 + )` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var followers []models.UserModel + for rows.Next() { + var follower models.UserModel + if err := rows.Scan( + &follower.ID, + &follower.Username, + &follower.Email, + &follower.Password, + &follower.FirstName, + &follower.LastName, + &follower.BirthDate, + &follower.Role, + &follower.Verified, + &follower.About, + &follower.CreatedAt, + &follower.UpdatedAt, + &follower.GoogleId, + &follower.AvatarUrl, + ); err != nil { + return nil, err + } + followers = append(followers, follower) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return followers, nil +} + +// retrieves events from database that match with follower_id +func (r *sqlEventFollowerRepository) GetEventsByFollowerId(follower_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events + WHERE id IN ( + SELECT event_id + FROM public.event_followers + WHERE follower_id = $1 + )` + + rows, err := r.database.Query(query, follower_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// deletes an event_follower record from the database. +func (r *sqlEventFollowerRepository) DeleteEventFollower(event_id string, follower_id string) error { + query := `DELETE FROM public.event_followers WHERE event_id = $1 AND follower_id = $2` + + rs, err := r.database.Exec(query, event_id, follower_id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventFollowerNotFound + } + + return nil +} + +// errors +var ( + ErrEventFollowerNotFound = errors.New("event_follower not found") + ErrInvalidEventFollowerId = errors.New("invalid event_follower id") +) diff --git a/pkg/repository/event_like.go b/pkg/repository/event_like.go new file mode 100644 index 0000000..baf8cf5 --- /dev/null +++ b/pkg/repository/event_like.go @@ -0,0 +1,221 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventLikeRepository represents the interface for event_like-related database operations. +type EventLikeRepository interface { + CreateEventLike(obj *models.EventLikeModel) error + GetEventLike(event_id string, user_id string) (*models.EventLikeModel, error) + GetUsersByEventId(event_id string) ([]models.UserModel, error) + GetEventsByUserId(user_id string) ([]models.EventModel, error) + DeleteEventLike(event_id string, user_id string) error +} + +type sqlEventLikeRepository struct { + database *sql.DB +} + +func NewSQLEventLikeRepository(database *sql.DB) EventLikeRepository { + return &sqlEventLikeRepository{database: database} +} + +// inserts a new event_like record into the database +func (r *sqlEventLikeRepository) CreateEventLike(obj *models.EventLikeModel) error { + obj.BeforeCreate() + + query := `INSERT INTO public.event_likes (event_id, user_id) + VALUES ($1, $2)` + + _, err := r.database.Exec(query, obj.EventId, obj.UserId) + if err != nil { + return fmt.Errorf("failed to create event_like: %w", err) + } + + obj.AfterCreate() + return nil +} + +// retrieves an event_like record from the database using event_id and user_id, if it exists +func (r *sqlEventLikeRepository) GetEventLike(event_id string, user_id string) (*models.EventLikeModel, error) { + query := `SELECT + event_id, + user_id + + FROM public.event_likes WHERE event_id = $1 AND user_id = $2` + + obj := &models.EventLikeModel{} + err := r.database.QueryRow(query, event_id, user_id).Scan( + &obj.EventId, + &obj.UserId, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventLikeNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventLikeId + } + + return obj, nil +} + +// retrieves users from database that have liked the given event +func (r *sqlEventLikeRepository) GetUsersByEventId(event_id string) ([]models.UserModel, error) { + query := `SELECT + id, + username, + email, + password, + first_name, + last_name, + birth_date, + role, + verified, + about, + created_at, + updated_at, + google_id, + avatar_url + + FROM public.users + WHERE id IN ( + SELECT user_id + FROM public.event_likes + WHERE event_id = $1 + )` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var users []models.UserModel + for rows.Next() { + var user models.UserModel + if err := rows.Scan( + &user.ID, + &user.Username, + &user.Email, + &user.Password, + &user.FirstName, + &user.LastName, + &user.BirthDate, + &user.Role, + &user.Verified, + &user.About, + &user.CreatedAt, + &user.UpdatedAt, + &user.GoogleId, + &user.AvatarUrl, + ); err != nil { + return nil, err + } + users = append(users, user) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return users, nil +} + +// retrieves events from database that match with user_id +func (r *sqlEventLikeRepository) GetEventsByUserId(user_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events + WHERE id IN ( + SELECT event_id + FROM public.event_likes + WHERE user_id = $1 + )` + + rows, err := r.database.Query(query, user_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// deletes an event_like record from the database. +func (r *sqlEventLikeRepository) DeleteEventLike(event_id string, user_id string) error { + query := `DELETE FROM public.event_likes WHERE event_id = $1 AND user_id = $2` + + rs, err := r.database.Exec(query, event_id, user_id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventLikeNotFound + } + + return nil +} + +// errors +var ( + ErrEventLikeNotFound = errors.New("event_like not found") + ErrInvalidEventLikeId = errors.New("invalid event_like id") +) diff --git a/pkg/repository/event_tag.go b/pkg/repository/event_tag.go new file mode 100644 index 0000000..f5fcb13 --- /dev/null +++ b/pkg/repository/event_tag.go @@ -0,0 +1,197 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// EventTagRepository represents the interface for event_tag-related database operations. +type EventTagRepository interface { + CreateEventTag(obj *models.EventTagModel) error + GetEventTag(event_id string, tag_id string) (*models.EventTagModel, error) + GetTagsByEventId(event_id string) ([]models.TagModel, error) + GetEventsByTagId(tag_id string) ([]models.EventModel, error) + DeleteEventTag(event_id string, tag_id string) error +} + +type sqlEventTagRepository struct { + database *sql.DB +} + +func NewSQLEventTagRepository(database *sql.DB) EventTagRepository { + return &sqlEventTagRepository{database: database} +} + +// inserts a new event_tag into the database +func (r *sqlEventTagRepository) CreateEventTag(obj *models.EventTagModel) error { + obj.BeforeCreate() + + query := `INSERT INTO public.event_tags (event_id, tag_id) + VALUES ($1, $2)` + + _, err := r.database.Exec(query, obj.EventId, obj.TagId) + if err != nil { + return fmt.Errorf("failed to create event_tag: %w", err) + } + + obj.AfterCreate() + return nil +} + +// retrieves an event_tag from the database using its event_id and tag_id, if it exists +func (r *sqlEventTagRepository) GetEventTag(event_id string, tag_id string) (*models.EventTagModel, error) { + query := `SELECT + event_id, + tag_id + + FROM public.event_tags WHERE event_id = $1 AND tag_id = $2` + + obj := &models.EventTagModel{} + err := r.database.QueryRow(query, event_id, tag_id).Scan( + &obj.EventId, + &obj.TagId, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrEventTagNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidEventTagId + } + + return obj, nil +} + +// retrieves tags from the database that are associated with the given event +func (r *sqlEventTagRepository) GetTagsByEventId(event_id string) ([]models.TagModel, error) { + query := `SELECT + id, + name + + FROM public.tags + WHERE id IN ( + SELECT tag_id + FROM public.event_tags + WHERE event_id = $1 + )` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var tags []models.TagModel + for rows.Next() { + var tag models.TagModel + if err := rows.Scan( + &tag.ID, + &tag.Name, + ); err != nil { + return nil, err + } + tags = append(tags, tag) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return tags, nil +} + +// retrieves events from database that match with tag_id +func (r *sqlEventTagRepository) GetEventsByTagId(tag_id string) ([]models.EventModel, error) { + query := `SELECT + id, + name, + organizer_id, + description, + start_date, + end_date, + is_paid, + event_type, + country, + city, + slug, + likes, + follows, + attendees, + created_at, + updated_at + + FROM public.events + WHERE id IN ( + SELECT event_id + FROM public.event_tags + WHERE tag_id = $1 + )` + + rows, err := r.database.Query(query, tag_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var events []models.EventModel + for rows.Next() { + var event models.EventModel + if err := rows.Scan( + &event.ID, + &event.Name, + &event.OrganizerId, + &event.Description, + &event.StartDate, + &event.EndDate, + &event.IsPaid, + &event.EventType, + &event.Country, + &event.City, + &event.Slug, + &event.Likes, + &event.Follows, + &event.Attendees, + &event.CreatedAt, + &event.UpdatedAt, + ); err != nil { + return nil, err + } + events = append(events, event) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return events, nil +} + +// deletes an event_tag from the database. +func (r *sqlEventTagRepository) DeleteEventTag(event_id string, tag_id string) error { + query := `DELETE FROM public.event_tags WHERE event_id = $1 AND tag_id = $2` + + rs, err := r.database.Exec(query, event_id, tag_id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrEventTagNotFound + } + + return nil +} + +// errors +var ( + ErrEventTagNotFound = errors.New("event_tag not found") + ErrInvalidEventTagId = errors.New("invalid event_tag id") +) diff --git a/pkg/repository/review.go b/pkg/repository/review.go new file mode 100644 index 0000000..5121646 --- /dev/null +++ b/pkg/repository/review.go @@ -0,0 +1,190 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// ReviewRepository represents the interface for review-related database operations. +type ReviewRepository interface { + CreateReview(review *models.ReviewModel) error + GetReviewByID(id string) (*models.ReviewModel, error) + GetReviewsByEventId(event_id string) ([]models.ReviewModel, error) + UpdateReview(review *models.ReviewModel) error + DeleteReview(id string) error +} + +type sqlReviewRepository struct { + database *sql.DB +} + +// NewSQLReviewRepository creates and returns a new sql flavoured ReviewRepository instance. +func NewSQLReviewRepository(database *sql.DB) ReviewRepository { + return &sqlReviewRepository{database: database} +} + +// inserts a new review into the database +func (r *sqlReviewRepository) CreateReview(review *models.ReviewModel) error { + review.BeforeCreate() + + query := `INSERT INTO public.reviews (title, event_id, author_id, body) + VALUES ($1, $2, $3, $4) RETURNING id` + + err := r.database.QueryRow(query, review.Title, review.EventId, review.AuthorId, review.Body).Scan(&review.ID) + if err != nil { + return fmt.Errorf("failed to create review: %w", err) + } + + review.AfterCreate() + return nil +} + +// retrieves a review from the database by its unique ID if it exists +func (r *sqlReviewRepository) GetReviewByID(id string) (*models.ReviewModel, error) { + query := `SELECT + id, + title, + event_id, + author_id, + body, + created_at, + updated_at + + FROM public.reviews WHERE id = $1` + + review := &models.ReviewModel{} + err := r.database.QueryRow(query, id).Scan( + &review.ID, + &review.Title, + &review.EventId, + &review.AuthorId, + &review.Body, + &review.CreatedAt, + &review.UpdatedAt, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrReviewNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidReviewId + } + + return review, nil +} + +// retrieves review(s) from database that match with event_id +func (r *sqlReviewRepository) GetReviewsByEventId(event_id string) ([]models.ReviewModel, error) { + query := `SELECT + id, + title, + event_id, + author_id, + body, + created_at, + updated_at + + FROM public.reviews WHERE event_id = $1` + + rows, err := r.database.Query(query, event_id) + if err != nil { + return nil, err + } + defer rows.Close() + + var reviews []models.ReviewModel + for rows.Next() { + var review models.ReviewModel + if err := rows.Scan( + &review.ID, + &review.Title, + &review.EventId, + &review.AuthorId, + &review.Body, + &review.CreatedAt, + &review.UpdatedAt, + ); err != nil { + return nil, err + } + reviews = append(reviews, review) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return reviews, nil +} + +// updates a review in the database +func (r *sqlReviewRepository) UpdateReview(review *models.ReviewModel) error { + review.BeforeUpdate() + + query := `UPDATE public.reviews SET + title = $1, + event_id = $2, + author_id = $3, + body = $4, + updated_at = $5 + + WHERE id = $6` + + // prevents a partial review from being submitted + if review.CreatedAt.Unix() == 0 { + return fmt.Errorf("unable to update a review that was not loaded from the database") + } + + rs, err := r.database.Exec( + query, + review.Title, + review.EventId, + review.AuthorId, + review.Body, + review.UpdatedAt, + review.ID, + ) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrReviewNotFound + } + + review.AfterUpdate() + return nil +} + +// deletes a review from the database. +func (r *sqlReviewRepository) DeleteReview(id string) error { + query := `DELETE FROM public.reviews WHERE id = $1` + + rs, err := r.database.Exec(query, id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrReviewNotFound + } + + return nil +} + +// errors +var ( + ErrReviewNotFound = errors.New("review not found") + ErrInvalidReviewId = errors.New("invalid review id") +) diff --git a/pkg/repository/tag.go b/pkg/repository/tag.go new file mode 100644 index 0000000..36bf42c --- /dev/null +++ b/pkg/repository/tag.go @@ -0,0 +1,118 @@ +package repository + +import ( + "database/sql" + "errors" + "fmt" + "net" + "reflect" + + "github.com/BEOpenSourceCollabs/EventManagementCore/pkg/models" +) + +// TagRepository represents the interface for tag-related database operations. +type TagRepository interface { + CreateTag(tag *models.TagModel) error + GetTagByID(id string) (*models.TagModel, error) + UpdateTag(tag *models.TagModel) error + DeleteTag(id string) error +} + +type sqlTagRepository struct { + database *sql.DB +} + +// NewSQLTagRepository creates and returns a new sql flavoured TagRepository instance. +func NewSQLTagRepository(database *sql.DB) TagRepository { + return &sqlTagRepository{database: database} +} + +// inserts a new tag into the database +func (r *sqlTagRepository) CreateTag(tag *models.TagModel) error { + tag.BeforeCreate() + + query := `INSERT INTO public.tags (name) VALUES ($1) RETURNING id` + + err := r.database.QueryRow(query, tag.Name).Scan(&tag.ID) + if err != nil { + return fmt.Errorf("failed to create tag: %w", err) + } + + tag.AfterCreate() + return nil +} + +// retrieves a tag from the database by its unique ID, if it exists +func (r *sqlTagRepository) GetTagByID(id string) (*models.TagModel, error) { + query := `SELECT + id, + name + + FROM public.tags WHERE id = $1` + + tag := &models.TagModel{} + err := r.database.QueryRow(query, id).Scan( + &tag.ID, + &tag.Name, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrTagNotFound + } + if reflect.TypeOf(err) == reflect.TypeOf(&net.OpError{}) { + return nil, ErrRepoConnErr + } + return nil, ErrInvalidTagId + } + + return tag, nil +} + +// updates a tag in database +func (r *sqlTagRepository) UpdateTag(tag *models.TagModel) error { + tag.BeforeUpdate() + + query := `UPDATE public.tags SET name = $1 WHERE id = $2` + rs, err := r.database.Exec( + query, + tag.Name, + tag.ID, + ) + if err != nil { + return err + } + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrTagNotFound + } + + tag.AfterUpdate() + return nil +} + +// deletes a tag from the database. +func (r *sqlTagRepository) DeleteTag(id string) error { + query := `DELETE FROM public.tags WHERE id = $1` + + rs, err := r.database.Exec(query, id) + if err != nil { + return err + } + + if affected, err := rs.RowsAffected(); affected < 1 { + if err != nil { + return err + } + return ErrTagNotFound + } + + return nil +} + +// errors +var ( + ErrTagNotFound = errors.New("tag not found") + ErrInvalidTagId = errors.New("invalid tag id") +) diff --git a/pkg/types/event_types.go b/pkg/types/event_types.go new file mode 100644 index 0000000..fa80f4d --- /dev/null +++ b/pkg/types/event_types.go @@ -0,0 +1,19 @@ +package types + +// Represents various types of events +type EventType string + +func (event_type EventType) IsValid() bool { + switch event_type { + case OfflineEventType, OnlineEventType, BothEventType: + return true + default: + return false + } +} + +const ( + OfflineEventType EventType = "offline" + OnlineEventType EventType = "online" + BothEventType EventType = "both" +)