Compare commits
No commits in common. "f798869ef5fcb9722d5b210b4c09a437e7a47c06" and "c2ab455d3e653b8d7cab9e2df812a45d6e033116" have entirely different histories.
f798869ef5
...
c2ab455d3e
@ -1,68 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var embedMigrations embed.FS
|
||||
var client *gorm.DB
|
||||
|
||||
func Connect(_ context.Context, config models.DatabaseConfig) error {
|
||||
var localSSL string
|
||||
|
||||
if config.SSL {
|
||||
localSSL = "require"
|
||||
} else {
|
||||
localSSL = "disable"
|
||||
}
|
||||
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s", config.Endpoint, config.Username, config.Password, config.Database, config.Port, localSSL, config.Timezone)
|
||||
sqlDB, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client = sqlDB
|
||||
err = migrateDatabase(sqlDB, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateDatabase(dbPool *gorm.DB, config models.DatabaseConfig) error {
|
||||
dialect := "postgres"
|
||||
migrations := &migrate.EmbedFileSystemMigrationSource{FileSystem: embedMigrations, Root: "migrations"}
|
||||
|
||||
db, err := dbPool.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("postgres migration: %v", err)
|
||||
}
|
||||
|
||||
n, err := migrate.Exec(db, dialect, migrations, migrate.Up)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postgres migration: %v", err)
|
||||
}
|
||||
|
||||
if config.Debug {
|
||||
if n != 0 {
|
||||
log.Infof("postgres migration: applied %d migrations!", n)
|
||||
|
||||
} else {
|
||||
log.Info("postgres migration: nothing to migrate")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
36
pkg/database/database.go
Normal file
36
pkg/database/database.go
Normal file
@ -0,0 +1,36 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type OtterSpace interface {
|
||||
// Connect establishes a connection to the database.
|
||||
Connect(ctx context.Context, config models.DatabaseConfig) error
|
||||
|
||||
// Post contains all function that are needed to manage Posts
|
||||
Post
|
||||
|
||||
// User contains all function that are needed to manage the AnthroveUser
|
||||
User
|
||||
|
||||
// Source contains all function that are needed to manage the Source
|
||||
Source
|
||||
|
||||
// Tag contains all functions that are used to manage Tag
|
||||
Tag
|
||||
|
||||
// TagAlias contains all function that are needed to manage the TagAlias
|
||||
TagAlias
|
||||
|
||||
// TagGroup contains all function that are needed to manage the TagGroup
|
||||
TagGroup
|
||||
|
||||
// ExecuteRawStatement run a custom query.
|
||||
ExecuteRawStatement(ctx context.Context, query string, args ...any) error
|
||||
|
||||
// QueryRawStatement runs a custom query and returns the table
|
||||
QueryRawStatement(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
||||
}
|
@ -79,12 +79,16 @@ CREATE TABLE "TagGroup"
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE "UserFavorites"
|
||||
(
|
||||
user_id TEXT REFERENCES "User" (id),
|
||||
post_id TEXT REFERENCES "Post" (id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, post_id)
|
||||
);
|
||||
|
||||
CREATE TABLE "UserSource"
|
||||
(
|
||||
id CHAR(25) PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NULL NULL,
|
||||
user_id TEXT REFERENCES "User" (id),
|
||||
source_id TEXT REFERENCES "Source" (id),
|
||||
scrape_time_interval INT,
|
||||
@ -93,20 +97,10 @@ CREATE TABLE "UserSource"
|
||||
last_scrape_time TIMESTAMP,
|
||||
account_validate BOOL DEFAULT FALSE,
|
||||
account_validation_key CHAR(25),
|
||||
PRIMARY KEY (user_id, source_id),
|
||||
UNIQUE (source_id, account_username, account_id)
|
||||
);
|
||||
|
||||
CREATE TABLE "UserFavorites"
|
||||
(
|
||||
id CHAR(25) PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NULL NULL,
|
||||
user_id TEXT REFERENCES "User" (id),
|
||||
post_id TEXT REFERENCES "Post" (id),
|
||||
user_source_id CHAR(25) REFERENCES "UserSource" (id)
|
||||
);
|
||||
|
||||
CREATE TABLE "post_tags"
|
||||
(
|
||||
post_id TEXT REFERENCES "Post" (id),
|
||||
|
31
pkg/database/post.go
Normal file
31
pkg/database/post.go
Normal file
@ -0,0 +1,31 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type Post interface {
|
||||
|
||||
// CreatePost adds a new post to the database.
|
||||
CreatePost(ctx context.Context, anthrovePost *models.Post) error
|
||||
|
||||
// TODO: Everything
|
||||
CreatePostInBatch(ctx context.Context, anthrovePost []models.Post, batchSize int) error
|
||||
|
||||
// GetPostByAnthroveID retrieves a post by its Anthrove ID.
|
||||
GetPostByAnthroveID(ctx context.Context, anthrovePostID models.AnthrovePostID) (*models.Post, error)
|
||||
|
||||
// GetPostByURL retrieves a post by its source URL.
|
||||
GetPostByURL(ctx context.Context, postURL string) (*models.Post, error)
|
||||
|
||||
// GetPostBySourceID retrieves a post by its source ID.
|
||||
GetPostBySourceID(ctx context.Context, sourceID models.AnthroveSourceID) (*models.Post, error)
|
||||
|
||||
// CreatePostWithReferenceToTagAnd adds a tag with a relation to a post.
|
||||
CreatePostWithReferenceToTagAnd(ctx context.Context, anthrovePostID models.AnthrovePostID, anthroveTag *models.Tag) error
|
||||
|
||||
// CreatePostReference links a post with a source.
|
||||
CreatePostReference(ctx context.Context, anthrovePostID models.AnthrovePostID, sourceDomain models.AnthroveSourceDomain, postURL models.AnthrovePostURL, config models.PostReferenceConfig) error
|
||||
}
|
268
pkg/database/postgres.go
Normal file
268
pkg/database/postgres.go
Normal file
@ -0,0 +1,268 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
log2 "log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/internal/postgres"
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
_ "github.com/lib/pq"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
log "github.com/sirupsen/logrus"
|
||||
gormPostgres "gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
gormLogger "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var embedMigrations embed.FS
|
||||
|
||||
type postgresqlConnection struct {
|
||||
db *gorm.DB
|
||||
debug bool
|
||||
}
|
||||
|
||||
func NewPostgresqlConnection() OtterSpace {
|
||||
return &postgresqlConnection{
|
||||
db: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) Connect(_ context.Context, config models.DatabaseConfig) error {
|
||||
var localSSL string
|
||||
var logLevel gormLogger.LogLevel
|
||||
|
||||
if config.SSL {
|
||||
localSSL = "require"
|
||||
} else {
|
||||
localSSL = "disable"
|
||||
}
|
||||
|
||||
p.debug = config.Debug
|
||||
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s", config.Endpoint, config.Username, config.Password, config.Database, config.Port, localSSL, config.Timezone)
|
||||
var err error
|
||||
|
||||
if p.debug {
|
||||
logLevel = gormLogger.Info
|
||||
} else {
|
||||
logLevel = gormLogger.Silent
|
||||
}
|
||||
|
||||
dbLogger := gormLogger.New(log2.New(os.Stdout, "\r\n", log2.LstdFlags), gormLogger.Config{
|
||||
SlowThreshold: 200 * time.Millisecond,
|
||||
LogLevel: logLevel,
|
||||
IgnoreRecordNotFoundError: true,
|
||||
Colorful: true,
|
||||
})
|
||||
|
||||
db, err := gorm.Open(gormPostgres.Open(dsn), &gorm.Config{
|
||||
Logger: dbLogger,
|
||||
})
|
||||
p.db = db
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("OtterSpace: database connection established")
|
||||
|
||||
err = p.migrateDatabase(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("OtterSpace: migration compleate")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateUserWithRelationToSource(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, accountId string, accountUsername string) error {
|
||||
return postgres.CreateUserWithRelationToSource(ctx, p.db, anthroveUserID, sourceID, accountId, accountUsername)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateSource(ctx context.Context, anthroveSource *models.Source) error {
|
||||
return postgres.CreateSource(ctx, p.db, anthroveSource)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreatePost(ctx context.Context, anthrovePost *models.Post) error {
|
||||
return postgres.CreatePost(ctx, p.db, anthrovePost)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreatePostInBatch(ctx context.Context, anthrovePost []models.Post, batchSize int) error {
|
||||
return postgres.CreatePostInBatch(ctx, p.db, anthrovePost, batchSize)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreatePostWithReferenceToTagAnd(ctx context.Context, anthrovePostID models.AnthrovePostID, anthroveTag *models.Tag) error {
|
||||
return postgres.CreateTagAndReferenceToPost(ctx, p.db, anthrovePostID, anthroveTag)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreatePostReference(ctx context.Context, anthrovePostID models.AnthrovePostID, sourceDomain models.AnthroveSourceDomain, postURL models.AnthrovePostURL, config models.PostReferenceConfig) error {
|
||||
return postgres.CreateReferenceBetweenPostAndSource(ctx, p.db, anthrovePostID, sourceDomain, postURL, config)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateReferenceBetweenUserAndPost(ctx context.Context, anthroveUserID models.AnthroveUserID, anthrovePostID models.AnthrovePostID) error {
|
||||
return postgres.CreateReferenceBetweenUserAndPost(ctx, p.db, anthroveUserID, anthrovePostID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CheckIfUserHasPostAsFavorite(ctx context.Context, anthroveUserID models.AnthroveUserID, anthrovePostID models.AnthrovePostID) (bool, error) {
|
||||
return postgres.CheckReferenceBetweenUserAndPost(ctx, p.db, anthroveUserID, anthrovePostID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetPostByAnthroveID(ctx context.Context, anthrovePostID models.AnthrovePostID) (*models.Post, error) {
|
||||
return postgres.GetPostByAnthroveID(ctx, p.db, anthrovePostID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetPostByURL(ctx context.Context, sourceUrl string) (*models.Post, error) {
|
||||
return postgres.GetPostBySourceURL(ctx, p.db, sourceUrl)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetPostBySourceID(ctx context.Context, sourceID models.AnthroveSourceID) (*models.Post, error) {
|
||||
return postgres.GetPostBySourceID(ctx, p.db, sourceID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetUserFavoritesCount(ctx context.Context, anthroveUserID models.AnthroveUserID) (int64, error) {
|
||||
return postgres.GetUserFavoritesCount(ctx, p.db, anthroveUserID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllUserSources(ctx context.Context, anthroveUserID models.AnthroveUserID) (map[string]models.UserSource, error) {
|
||||
return postgres.GetUserSourceLinks(ctx, p.db, anthroveUserID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetUserSourceBySourceID(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID) (*models.UserSource, error) {
|
||||
return postgres.GetUserSourceBySourceID(ctx, p.db, anthroveUserID, sourceID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllUsers(ctx context.Context) ([]models.User, error) {
|
||||
return postgres.GetAllUsers(ctx, p.db)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllUserFavoritesWithPagination(ctx context.Context, anthroveUserID models.AnthroveUserID, skip int, limit int) (*models.FavoriteList, error) {
|
||||
return postgres.GetUserFavoriteWithPagination(ctx, p.db, anthroveUserID, skip, limit)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagsFromUser(ctx context.Context, anthroveUserID models.AnthroveUserID) ([]models.TagsWithFrequency, error) {
|
||||
return postgres.GetUserTagWitRelationToFavedPosts(ctx, p.db, anthroveUserID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTags(ctx context.Context) ([]models.Tag, error) {
|
||||
return postgres.GetTags(ctx, p.db)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllSources(ctx context.Context) ([]models.Source, error) {
|
||||
return postgres.GetAllSource(ctx, p.db)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetSourceByDomain(ctx context.Context, sourceDomain models.AnthroveSourceDomain) (*models.Source, error) {
|
||||
return postgres.GetSourceByDomain(ctx, p.db, sourceDomain)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) UpdateUserSourceScrapeTimeInterval(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, scrapeTime models.AnthroveScrapeTimeInterval) error {
|
||||
return postgres.UpdateUserSourceScrapeTimeInterval(ctx, p.db, anthroveUserID, sourceID, scrapeTime)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) UpdateUserSourceLastScrapeTime(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, lastScrapeTime models.AnthroveUserLastScrapeTime) error {
|
||||
return postgres.UpdateUserSourceLastScrapeTime(ctx, p.db, anthroveUserID, sourceID, lastScrapeTime)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) UpdateUserSourceValidation(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, valid bool) error {
|
||||
return postgres.UpdateUserSourceValidation(ctx, p.db, anthroveUserID, sourceID, valid)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTagAlias(ctx context.Context, tagAliasName models.AnthroveTagAliasName, tagID models.AnthroveTagID) error {
|
||||
return postgres.CreateTagAlias(ctx, p.db, tagAliasName, tagID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagAlias(ctx context.Context) ([]models.TagAlias, error) {
|
||||
return postgres.GetAllTagAlias(ctx, p.db)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagAliasByTag(ctx context.Context, tagID models.AnthroveTagID) ([]models.TagAlias, error) {
|
||||
return postgres.GetAllTagAliasByTag(ctx, p.db, tagID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) DeleteTagAlias(ctx context.Context, tagAliasName models.AnthroveTagAliasName) error {
|
||||
return postgres.DeleteTagAlias(ctx, p.db, tagAliasName)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTagGroup(ctx context.Context, tagGroupName models.AnthroveTagGroupName, tagID models.AnthroveTagID) error {
|
||||
return postgres.CreateTagGroup(ctx, p.db, tagGroupName, tagID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagGroup(ctx context.Context) ([]models.TagGroup, error) {
|
||||
return postgres.GetAllTagGroup(ctx, p.db)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagGroupByTag(ctx context.Context, tagID models.AnthroveTagID) ([]models.TagGroup, error) {
|
||||
return postgres.GetAllTagGroupByTag(ctx, p.db, tagID)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) DeleteTagGroup(ctx context.Context, tagGroupName models.AnthroveTagGroupName) error {
|
||||
return postgres.DeleteTagGroup(ctx, p.db, tagGroupName)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTag(ctx context.Context, tagName models.AnthroveTagName, tagType models.TagType) error {
|
||||
return postgres.CreateTag(ctx, p.db, tagName, tagType)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) GetAllTagsByTagType(ctx context.Context, tagType models.TagType) ([]models.Tag, error) {
|
||||
return postgres.GetAllTagByTagsType(ctx, p.db, tagType)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) DeleteTag(ctx context.Context, tagName models.AnthroveTagName) error {
|
||||
return postgres.DeleteTag(ctx, p.db, tagName)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTagInBatchAndUpdate(ctx context.Context, tags []models.Tag, batchSize int) error {
|
||||
return postgres.CreateTagInBatchAndUpdate(ctx, p.db, tags, batchSize)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTagAliasInBatch(ctx context.Context, tagAliases []models.TagAlias, batchSize int) error {
|
||||
return postgres.CreateTagAliasInBatch(ctx, p.db, tagAliases, batchSize)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) CreateTagGroupInBatch(ctx context.Context, tagGroups []models.TagGroup, batchSize int) error {
|
||||
return postgres.CreateTagGroupInBatch(ctx, p.db, tagGroups, batchSize)
|
||||
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) ExecuteRawStatement(ctx context.Context, query string, args ...any) error {
|
||||
return postgres.ExecuteRawStatement(ctx, p.db, query, args...)
|
||||
}
|
||||
|
||||
func (p *postgresqlConnection) QueryRawStatement(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
|
||||
return postgres.QueryRawStatement(ctx, p.db, query, args...)
|
||||
}
|
||||
|
||||
// HELPER
|
||||
|
||||
func (p *postgresqlConnection) migrateDatabase(dbPool *gorm.DB) error {
|
||||
dialect := "postgres"
|
||||
migrations := &migrate.EmbedFileSystemMigrationSource{FileSystem: embedMigrations, Root: "migrations"}
|
||||
|
||||
db, err := dbPool.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("postgres migration: %v", err)
|
||||
}
|
||||
|
||||
n, err := migrate.Exec(db, dialect, migrations, migrate.Up)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postgres migration: %v", err)
|
||||
}
|
||||
|
||||
if p.debug {
|
||||
if n != 0 {
|
||||
log.Infof("postgres migration: applied %d migrations!", n)
|
||||
|
||||
} else {
|
||||
log.Info("postgres migration: nothing to migrate")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
3791
pkg/database/postgres_test.go
Normal file
3791
pkg/database/postgres_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,107 +2,18 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
otterError "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/error"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreateUserSource(ctx context.Context, userSource models.UserSource) (models.UserSource, error) {
|
||||
if client == nil {
|
||||
return models.UserSource{}, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}
|
||||
}
|
||||
type Source interface {
|
||||
|
||||
result := client.WithContext(ctx).Create(&userSource)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrDuplicatedKey) {
|
||||
return models.UserSource{}, &otterError.Database{Reason: otterError.DuplicateKey}
|
||||
}
|
||||
return models.UserSource{}, result.Error
|
||||
}
|
||||
// CreateSource adds a new source to the database.
|
||||
CreateSource(ctx context.Context, anthroveSource *models.Source) error
|
||||
|
||||
return userSource, nil
|
||||
}
|
||||
|
||||
func UpdateUserSource(ctx context.Context, userSource models.UserSource) error {
|
||||
if client == nil {
|
||||
return &otterError.Database{Reason: otterError.DatabaseIsNotConnected}
|
||||
}
|
||||
|
||||
if len(userSource.ID) == 0 {
|
||||
return &otterError.EntityValidationFailed{Reason: otterError.UserSourceIDEmpty}
|
||||
}
|
||||
|
||||
updatedUserSource := models.UserSource{
|
||||
BaseModel: models.BaseModel[models.UserSourceID]{
|
||||
ID: userSource.ID,
|
||||
},
|
||||
ScrapeTimeInterval: userSource.ScrapeTimeInterval,
|
||||
AccountUsername: userSource.AccountUsername,
|
||||
AccountID: userSource.AccountID,
|
||||
LastScrapeTime: userSource.LastScrapeTime,
|
||||
AccountValidate: userSource.AccountValidate,
|
||||
}
|
||||
|
||||
result := client.WithContext(ctx).Updates(&updatedUserSource)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrDuplicatedKey) {
|
||||
return &otterError.Database{Reason: otterError.DuplicateKey}
|
||||
}
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserSourceByID(ctx context.Context, id models.UserSourceID) (models.UserSource, error) {
|
||||
var user models.UserSource
|
||||
|
||||
if client == nil {
|
||||
return models.UserSource{}, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}
|
||||
}
|
||||
|
||||
if len(id) == 0 {
|
||||
return models.UserSource{}, &otterError.EntityValidationFailed{Reason: otterError.UserSourceIDEmpty}
|
||||
}
|
||||
|
||||
if len(id) != 25 {
|
||||
return models.UserSource{}, &otterError.EntityValidationFailed{Reason: otterError.UserSourceIDToShort}
|
||||
}
|
||||
|
||||
result := client.WithContext(ctx).First(&user, id)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return models.UserSource{}, &otterError.Database{Reason: otterError.NoDataFound}
|
||||
}
|
||||
return models.UserSource{}, result.Error
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func DeleteUserSource(ctx context.Context, id models.UserSourceID) error {
|
||||
var user models.UserSource
|
||||
|
||||
if client == nil {
|
||||
return &otterError.Database{Reason: otterError.DatabaseIsNotConnected}
|
||||
}
|
||||
|
||||
if len(id) == 0 {
|
||||
return &otterError.EntityValidationFailed{Reason: otterError.UserSourceIDEmpty}
|
||||
}
|
||||
|
||||
if len(id) != 25 {
|
||||
return &otterError.EntityValidationFailed{Reason: otterError.UserSourceIDToShort}
|
||||
}
|
||||
|
||||
result := client.WithContext(ctx).Delete(&user, id)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return &otterError.Database{Reason: otterError.NoDataFound}
|
||||
}
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
// GetAllSources retrieves all sources.
|
||||
GetAllSources(ctx context.Context) ([]models.Source, error)
|
||||
|
||||
// GetSourceByDomain retrieves a source by its URL.
|
||||
GetSourceByDomain(ctx context.Context, sourceDomain models.AnthroveSourceDomain) (*models.Source, error)
|
||||
}
|
||||
|
20
pkg/database/tag.go
Normal file
20
pkg/database/tag.go
Normal file
@ -0,0 +1,20 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type Tag interface {
|
||||
CreateTag(ctx context.Context, tagName models.AnthroveTagName, tagType models.TagType) error
|
||||
|
||||
CreateTagInBatchAndUpdate(ctx context.Context, tags []models.Tag, batchSize int) error
|
||||
|
||||
// GetAllTags retrieves all tags.
|
||||
GetAllTags(ctx context.Context) ([]models.Tag, error)
|
||||
|
||||
GetAllTagsByTagType(ctx context.Context, tagType models.TagType) ([]models.Tag, error)
|
||||
|
||||
DeleteTag(ctx context.Context, tagName models.AnthroveTagName) error
|
||||
}
|
19
pkg/database/tagalias.go
Normal file
19
pkg/database/tagalias.go
Normal file
@ -0,0 +1,19 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type TagAlias interface {
|
||||
CreateTagAlias(ctx context.Context, tagAliasName models.AnthroveTagAliasName, tagID models.AnthroveTagID) error
|
||||
|
||||
CreateTagAliasInBatch(ctx context.Context, tagsAliases []models.TagAlias, batchSize int) error
|
||||
|
||||
GetAllTagAlias(ctx context.Context) ([]models.TagAlias, error)
|
||||
|
||||
GetAllTagAliasByTag(ctx context.Context, tagID models.AnthroveTagID) ([]models.TagAlias, error)
|
||||
|
||||
DeleteTagAlias(ctx context.Context, tagAliasName models.AnthroveTagAliasName) error
|
||||
}
|
19
pkg/database/taggroup.go
Normal file
19
pkg/database/taggroup.go
Normal file
@ -0,0 +1,19 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type TagGroup interface {
|
||||
CreateTagGroup(ctx context.Context, tagGroupName models.AnthroveTagGroupName, tagID models.AnthroveTagID) error
|
||||
|
||||
CreateTagGroupInBatch(ctx context.Context, tagsGroups []models.TagGroup, batchSize int) error
|
||||
|
||||
GetAllTagGroup(ctx context.Context) ([]models.TagGroup, error)
|
||||
|
||||
GetAllTagGroupByTag(ctx context.Context, tagID models.AnthroveTagID) ([]models.TagGroup, error)
|
||||
|
||||
DeleteTagGroup(ctx context.Context, tagGroupName models.AnthroveTagGroupName) error
|
||||
}
|
42
pkg/database/user.go
Normal file
42
pkg/database/user.go
Normal file
@ -0,0 +1,42 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
|
||||
)
|
||||
|
||||
type User interface {
|
||||
|
||||
// CreateUserWithRelationToSource adds a user with a relation to a source.
|
||||
CreateUserWithRelationToSource(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, accountId string, accountUsername string) error
|
||||
|
||||
// CreateReferenceBetweenUserAndPost links a user with a post.
|
||||
CreateReferenceBetweenUserAndPost(ctx context.Context, anthroveUserID models.AnthroveUserID, anthrovePostID models.AnthrovePostID) error
|
||||
|
||||
UpdateUserSourceScrapeTimeInterval(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, scrapeTime models.AnthroveScrapeTimeInterval) error
|
||||
|
||||
UpdateUserSourceLastScrapeTime(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, lastScrapeTime models.AnthroveUserLastScrapeTime) error
|
||||
|
||||
UpdateUserSourceValidation(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, valid bool) error
|
||||
|
||||
GetAllUsers(ctx context.Context) ([]models.User, error)
|
||||
|
||||
// GetUserFavoritesCount retrieves the count of a user's favorites.
|
||||
GetUserFavoritesCount(ctx context.Context, anthroveUserID models.AnthroveUserID) (int64, error)
|
||||
|
||||
// GetAllUserSources retrieves the source links of a user.
|
||||
GetAllUserSources(ctx context.Context, anthroveUserID models.AnthroveUserID) (map[string]models.UserSource, error)
|
||||
|
||||
// GetUserSourceBySourceID retrieves a specified source link of a user.
|
||||
GetUserSourceBySourceID(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID) (*models.UserSource, error)
|
||||
|
||||
// GetAllUserFavoritesWithPagination retrieves a user's favorite posts with pagination.
|
||||
GetAllUserFavoritesWithPagination(ctx context.Context, anthroveUserID models.AnthroveUserID, skip int, limit int) (*models.FavoriteList, error)
|
||||
|
||||
// GetAllTagsFromUser retrieves a user's tags through their favorite posts.
|
||||
GetAllTagsFromUser(ctx context.Context, anthroveUserID models.AnthroveUserID) ([]models.TagsWithFrequency, error)
|
||||
|
||||
// CheckIfUserHasPostAsFavorite checks if a user-post link exists.
|
||||
CheckIfUserHasPostAsFavorite(ctx context.Context, anthroveUserID models.AnthroveUserID, sourcePostID models.AnthrovePostID) (bool, error)
|
||||
}
|
@ -1,19 +1,25 @@
|
||||
package error
|
||||
|
||||
import "fmt"
|
||||
type EntityAlreadyExists struct{}
|
||||
|
||||
const (
|
||||
EntityAlreadyExists = "EntityAlreadyExists"
|
||||
NoDataWritten = "NoDataWritten"
|
||||
NoDataFound = "NoDataFound"
|
||||
DatabaseIsNotConnected = "database is not connected"
|
||||
DuplicateKey = "DuplicateKey"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
Reason string
|
||||
func (e *EntityAlreadyExists) Error() string {
|
||||
return "EntityAlreadyExists error"
|
||||
}
|
||||
|
||||
func (e Database) Error() string {
|
||||
return fmt.Sprintf("Database error: %s", e.Reason)
|
||||
type NoDataWritten struct{}
|
||||
|
||||
func (e *NoDataWritten) Error() string {
|
||||
return "NoDataWritten error"
|
||||
}
|
||||
|
||||
type NoDataFound struct{}
|
||||
|
||||
func (e *NoDataFound) Error() string {
|
||||
return "NoDataFound error"
|
||||
}
|
||||
|
||||
type NoRelationCreated struct{}
|
||||
|
||||
func (e *NoRelationCreated) Error() string {
|
||||
return "relationship creation error"
|
||||
}
|
||||
|
@ -3,13 +3,11 @@ package error
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
UserIDIsEmpty = "anthrovePostID cannot be empty"
|
||||
UserIDToShort = "anthrovePostID needs to be 25 characters long"
|
||||
SourceIDEmpty = "anthroveSourceID cannot be empty"
|
||||
SourceIDToShort = "anthroveSourceID needs to be 25 characters long"
|
||||
UserSourceIDEmpty = "anthroveUserSourceID cannot be empty"
|
||||
UserSourceIDToShort = "anthroveUserSourceID needs to be 25 characters long"
|
||||
TagIDEmpty = "tagID cannot be empty"
|
||||
AnthroveUserIDIsEmpty = "anthrovePostID cannot be empty"
|
||||
AnthroveUserIDToShort = "anthrovePostID needs to be 25 characters long"
|
||||
AnthroveSourceIDEmpty = "anthroveSourceID cannot be empty"
|
||||
AnthroveSourceIDToShort = "anthroveSourceID needs to be 25 characters long"
|
||||
AnthroveTagIDEmpty = "tagID cannot be empty"
|
||||
)
|
||||
|
||||
type EntityValidationFailed struct {
|
||||
|
@ -2,25 +2,20 @@ package models
|
||||
|
||||
import "time"
|
||||
|
||||
type (
|
||||
UserID string
|
||||
PostID string
|
||||
SourceID string
|
||||
SourceDomain string
|
||||
PostURL string
|
||||
TagGroupName string
|
||||
TagAliasName string
|
||||
TagID string
|
||||
ScrapeTimeInterval int
|
||||
UserLastScrapeTime time.Time
|
||||
TagName string
|
||||
type AnthroveUserID string
|
||||
type AnthrovePostID string
|
||||
type AnthroveSourceID string
|
||||
type AnthroveSourceDomain string
|
||||
type AnthrovePostURL string
|
||||
type AnthroveTagGroupName string
|
||||
type AnthroveTagAliasName string
|
||||
type AnthroveTagID string
|
||||
type AnthroveScrapeTimeInterval int
|
||||
type AnthroveUserLastScrapeTime time.Time
|
||||
type AnthroveTagName string
|
||||
|
||||
Rating string
|
||||
TagType string
|
||||
|
||||
UserSourceID string
|
||||
UserFavoriteID string
|
||||
)
|
||||
type Rating string
|
||||
type TagType string
|
||||
|
||||
const (
|
||||
SFW Rating = "safe"
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type ID interface {
|
||||
UserID | SourceID | PostID | UserSourceID | UserFavoriteID
|
||||
AnthroveUserID | AnthroveSourceID | AnthrovePostID
|
||||
}
|
||||
|
||||
type BaseModel[T ID] struct {
|
||||
|
@ -42,8 +42,8 @@ func TestBaseModel_BeforeCreate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
base := &BaseModel[PostID]{
|
||||
ID: PostID(tt.fields.ID),
|
||||
base := &BaseModel[AnthrovePostID]{
|
||||
ID: AnthrovePostID(tt.fields.ID),
|
||||
CreatedAt: tt.fields.CreatedAt,
|
||||
UpdatedAt: tt.fields.UpdatedAt,
|
||||
DeletedAt: tt.fields.DeletedAt,
|
||||
|
@ -2,7 +2,7 @@ package models
|
||||
|
||||
// Post model
|
||||
type Post struct {
|
||||
BaseModel[PostID]
|
||||
BaseModel[AnthrovePostID]
|
||||
Rating Rating `json:"rating" gorm:"type:enum('safe','questionable','explicit')"`
|
||||
Tags []Tag `json:"-" gorm:"many2many:post_tags;"`
|
||||
Favorites []UserFavorites `json:"-" gorm:"foreignKey:PostID"`
|
||||
|
@ -4,7 +4,7 @@ import "testing"
|
||||
|
||||
func TestPost_TableName(t *testing.T) {
|
||||
type fields struct {
|
||||
BaseModel BaseModel[PostID]
|
||||
BaseModel BaseModel[AnthrovePostID]
|
||||
Rating Rating
|
||||
Tags []Tag
|
||||
Favorites []UserFavorites
|
||||
|
@ -2,7 +2,7 @@ package models
|
||||
|
||||
// Source model
|
||||
type Source struct {
|
||||
BaseModel[SourceID]
|
||||
BaseModel[AnthroveSourceID]
|
||||
DisplayName string `json:"display_name" `
|
||||
Domain string `json:"domain" gorm:"not null;unique"`
|
||||
Icon string `json:"icon" gorm:"not null"`
|
||||
|
@ -4,7 +4,7 @@ import "testing"
|
||||
|
||||
func TestSource_TableName(t *testing.T) {
|
||||
type fields struct {
|
||||
BaseModel BaseModel[SourceID]
|
||||
BaseModel BaseModel[AnthroveSourceID]
|
||||
DisplayName string
|
||||
Domain string
|
||||
Icon string
|
||||
|
@ -2,7 +2,7 @@ package models
|
||||
|
||||
// User model
|
||||
type User struct {
|
||||
BaseModel[UserID]
|
||||
BaseModel[AnthroveUserID]
|
||||
Favorites []UserFavorites `json:"-" gorm:"foreignKey:UserID"`
|
||||
Sources []UserSource `json:"-" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
@ -3,12 +3,9 @@ package models
|
||||
import "time"
|
||||
|
||||
type UserFavorites struct {
|
||||
BaseModel[UserFavoriteID]
|
||||
UserID string `json:"user_id"`
|
||||
PostID string `json:"post_id"`
|
||||
UserSourceID UserSourceID `json:"user_source_id"`
|
||||
UserSource UserSource `json:"-" gorm:"foreignKey:ID;references:UserSourceID"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UserID string `json:"user_id" gorm:"primaryKey"`
|
||||
PostID string `json:"post_id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"-"`
|
||||
}
|
||||
|
||||
func (UserFavorites) TableName() string {
|
||||
|
@ -3,11 +3,10 @@ package models
|
||||
import "time"
|
||||
|
||||
type UserSource struct {
|
||||
BaseModel[UserSourceID]
|
||||
User User `json:"user" gorm:"foreignKey:ID;references:UserID"`
|
||||
UserID UserID `json:"user_id"`
|
||||
UserID string `json:"user_id" gorm:"primaryKey"`
|
||||
Source Source `json:"source" gorm:"foreignKey:ID;references:SourceID"`
|
||||
SourceID SourceID `json:"source_id"`
|
||||
SourceID string `json:"source_id" gorm:"primaryKey"`
|
||||
ScrapeTimeInterval string `json:"scrape_time_interval"`
|
||||
AccountUsername string `json:"account_username"`
|
||||
AccountID string `json:"account_id"`
|
||||
|
@ -5,9 +5,9 @@ import "testing"
|
||||
func TestUserSource_TableName(t *testing.T) {
|
||||
type fields struct {
|
||||
User User
|
||||
UserID UserID
|
||||
UserID string
|
||||
Source Source
|
||||
SourceID SourceID
|
||||
SourceID string
|
||||
ScrapeTimeInterval string
|
||||
AccountUsername string
|
||||
AccountID string
|
||||
|
@ -4,7 +4,7 @@ import "testing"
|
||||
|
||||
func TestUser_TableName(t *testing.T) {
|
||||
type fields struct {
|
||||
BaseModel BaseModel[UserID]
|
||||
BaseModel BaseModel[AnthroveUserID]
|
||||
Favorites []UserFavorites
|
||||
Sources []UserSource
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user