2024-07-19 08:03:35 +00:00
package postgres
import (
"context"
"errors"
"time"
2024-07-22 09:11:48 +00:00
otterError "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/error"
"git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models"
2024-07-19 08:03:35 +00:00
gonanoid "github.com/matoous/go-nanoid/v2"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
)
// Workaround, should be changed later maybe, but its not that bad right now
type selectFrequencyTag struct {
tagName string ` gorm:"tag_name" `
count int64 ` gorm:"count" `
tagType models . TagType ` gorm:"tag_type" `
}
func CreateUser ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID ) error {
if anthroveUserID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
user := models . User {
BaseModel : models . BaseModel [ models . AnthroveUserID ] {
ID : anthroveUserID ,
} ,
}
result := db . WithContext ( ctx ) . FirstOrCreate ( & user )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrDuplicatedKey ) {
return & otterError . EntityAlreadyExists { }
}
return result . Error
}
return nil
}
func CreateUserWithRelationToSource ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , sourceID models . AnthroveSourceID , accountId string , accountUsername string ) error {
if anthroveUserID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
if accountId == "" {
return & otterError . EntityValidationFailed { Reason : "accountID cannot be empty" }
}
if accountUsername == "" {
return & otterError . EntityValidationFailed { Reason : "accountUsername cannot be empty" }
}
validationCode , err := gonanoid . New ( 25 )
if err != nil {
return err
}
result := db . WithContext ( ctx ) . Exec ( ` WITH userObj AS (
INSERT INTO "User" ( id )
VALUES ( $ 1 )
ON CONFLICT ( id ) DO NOTHING
)
INSERT INTO "UserSource" ( user_id , source_id , account_username , account_id , account_validate , account_validation_key )
SELECT $ 2 , source . id , $ 3 , $ 4 , false , $ 5
FROM "Source" AS source
WHERE source . id = $ 6 ; ` , anthroveUserID , anthroveUserID , accountUsername , accountId , validationCode , sourceID )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrDuplicatedKey ) {
return & otterError . EntityAlreadyExists { }
}
return result . Error
}
if result . RowsAffected == 0 {
return & otterError . NoDataWritten { }
}
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
"source_id" : sourceID ,
"account_username" : accountUsername ,
"account_id" : accountId ,
} ) . Info ( "database: created user-source relationship" )
return nil
}
func GetUserFavoritesCount ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID ) ( int64 , error ) {
var count int64
if anthroveUserID == "" {
return 0 , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return 0 , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
result := db . WithContext ( ctx ) . Model ( & models . UserFavorites { } ) . Where ( "user_id = ?" , string ( anthroveUserID ) ) . Count ( & count )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrRecordNotFound ) {
return 0 , & otterError . NoDataFound { }
}
return 0 , result . Error
}
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
"anthrove_user_fav_count" : count ,
} ) . Trace ( "database: got user favorite count" )
return count , nil
}
func GetUserSourceLinks ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID ) ( map [ string ] models . UserSource , error ) {
var userSources [ ] models . UserSource
userSourceMap := make ( map [ string ] models . UserSource )
if anthroveUserID == "" {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
result := db . WithContext ( ctx ) . Model ( & models . UserSource { } ) . Where ( "user_id = ?" , string ( anthroveUserID ) ) . Find ( & userSources )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrRecordNotFound ) {
return nil , & otterError . NoDataFound { }
}
return nil , result . Error
}
for _ , userSource := range userSources {
var source models . Source
result = db . WithContext ( ctx ) . Model ( & models . Source { } ) . Where ( "id = ?" , userSource . SourceID ) . First ( & source )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrRecordNotFound ) {
return nil , & otterError . NoDataFound { }
}
return nil , result . Error
}
userSourceMap [ source . DisplayName ] = models . UserSource {
UserID : userSource . AccountID ,
AccountUsername : userSource . AccountUsername ,
Source : models . Source {
DisplayName : source . DisplayName ,
Domain : source . Domain ,
Icon : source . Icon ,
} ,
}
}
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
} ) . Trace ( "database: got user source link" )
return userSourceMap , nil
}
func GetUserSourceBySourceID ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , sourceID models . AnthroveSourceID ) ( * models . UserSource , error ) {
var userSource models . UserSource
if anthroveUserID == "" {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
if sourceID == "" {
return nil , & otterError . EntityValidationFailed { Reason : "sourceID cannot be empty" }
}
if len ( sourceID ) != 25 {
return nil , & otterError . EntityValidationFailed { Reason : "sourceID needs to be 25 characters long" }
}
result := db . WithContext ( ctx ) . Model ( & models . UserSource { } ) . InnerJoins ( "Source" , db . Where ( "id = ?" , sourceID ) ) . Where ( "user_id = ?" , string ( anthroveUserID ) ) . First ( & userSource )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrRecordNotFound ) {
return nil , & otterError . NoDataFound { }
}
return nil , result . Error
}
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
"source_id" : sourceID ,
} ) . Trace ( "database: got specified user source link" )
return & userSource , nil
}
func GetAllUsers ( ctx context . Context , db * gorm . DB ) ( [ ] models . User , error ) {
var users [ ] models . User
result := db . WithContext ( ctx ) . Model ( & models . User { } ) . Find ( & users )
if result . Error != nil {
if errors . Is ( result . Error , gorm . ErrRecordNotFound ) {
return nil , & otterError . NoDataFound { }
}
return nil , result . Error
}
log . WithFields ( log . Fields {
"anthrove_user_id_count" : len ( users ) ,
} ) . Trace ( "database: got all anthrove user IDs" )
return users , nil
}
// TODO: FIX THE TEST
func GetUserFavoriteWithPagination ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , skip int , limit int ) ( * models . FavoriteList , error ) {
var favoritePosts [ ] models . Post
if anthroveUserID == "" {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
db . WithContext ( ctx ) . Joins ( "RIGHT JOIN \"UserFavorites\" AS of ON \"Post\".id = of.post_id AND of.user_id = ?" , anthroveUserID ) . Preload ( "References" ) . Offset ( skip ) . Limit ( limit ) . Find ( & favoritePosts )
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
"anthrove_user_fav_count" : len ( favoritePosts ) ,
} ) . Trace ( "database: got all anthrove user favorites" )
return & models . FavoriteList { Posts : favoritePosts } , nil
}
func GetUserTagWitRelationToFavedPosts ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID ) ( [ ] models . TagsWithFrequency , error ) {
var queryUserFavorites [ ] selectFrequencyTag
if anthroveUserID == "" {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return nil , & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
rows , err := db . WithContext ( ctx ) . Raw (
` WITH user_posts AS (
SELECT post_id FROM "UserFavorites" WHERE user_id = $ 1
)
SELECT post_tags . tag_name AS tag_name , count ( * ) AS count , ( SELECT tag_type FROM "Tag" WHERE "Tag" . name = post_tags . tag_name LIMIT 1 ) AS tag_type FROM post_tags , user_posts WHERE post_tags . post_id IN ( user_posts . post_id ) GROUP BY post_tags . tag_name ORDER BY tag_type DESC , tag_name DESC ` , anthroveUserID ) . Rows ( )
if err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
return nil , & otterError . NoDataFound { }
}
return nil , err
}
var userFavoritesFrequency = make ( [ ] models . TagsWithFrequency , 0 )
defer rows . Close ( )
for rows . Next ( ) {
var tagName string
var count int64
var tagType string
rows . Scan ( & tagName , & count , & tagType )
userFavoritesFrequency = append ( userFavoritesFrequency , models . TagsWithFrequency {
Frequency : count ,
Tags : models . Tag {
Name : tagName ,
Type : models . TagType ( tagType ) ,
} ,
} )
}
log . WithFields ( log . Fields {
"anthrove_user_id" : anthroveUserID ,
"tag_amount" : len ( queryUserFavorites ) ,
} ) . Trace ( "database: got user tag node with relation to faved posts" )
return userFavoritesFrequency , nil
}
func UpdateUserSourceScrapeTimeInterval ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , sourceID models . AnthroveSourceID , scrapeTime models . AnthroveScrapeTimeInterval ) error {
if anthroveUserID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
if sourceID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDEmpty }
}
if len ( sourceID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDToShort }
}
if scrapeTime == 0 {
return & otterError . EntityValidationFailed { Reason : "ScrapeTimeInterval cannot be empty" }
}
userSource := & models . UserSource {
UserID : string ( anthroveUserID ) ,
}
result := db . WithContext ( ctx ) . Model ( & userSource ) . Update ( "scrape_time_interval" , scrapeTime )
if result . Error != nil {
return result . Error
}
return nil
}
func UpdateUserSourceLastScrapeTime ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , sourceID models . AnthroveSourceID , lastScrapeTime models . AnthroveUserLastScrapeTime ) error {
if anthroveUserID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
if sourceID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDEmpty }
}
if len ( sourceID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDToShort }
}
if time . Time . IsZero ( time . Time ( lastScrapeTime ) ) {
return & otterError . EntityValidationFailed { Reason : "LastScrapeTime cannot be empty" }
}
userSource := & models . UserSource {
UserID : string ( anthroveUserID ) ,
}
result := db . WithContext ( ctx ) . Model ( & userSource ) . Update ( "last_scrape_time" , time . Time ( lastScrapeTime ) )
if result . Error != nil {
return result . Error
}
return nil
}
func UpdateUserSourceValidation ( ctx context . Context , db * gorm . DB , anthroveUserID models . AnthroveUserID , sourceID models . AnthroveSourceID , valid bool ) error {
if anthroveUserID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDIsEmpty }
}
if len ( anthroveUserID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveUserIDToShort }
}
if sourceID == "" {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDEmpty }
}
if len ( sourceID ) != 25 {
return & otterError . EntityValidationFailed { Reason : otterError . AnthroveSourceIDToShort }
}
userSource := & models . UserSource {
UserID : string ( anthroveUserID ) ,
}
result := db . WithContext ( ctx ) . Model ( & userSource ) . Update ( "account_validate" , valid )
if result . Error != nil {
return result . Error
}
return nil
}