BREAKING CHANGE: V2 of thr SDK #12

Merged
fenpaws merged 124 commits from develop/postgresql into main 2024-07-01 20:46:28 +00:00
6 changed files with 831 additions and 9 deletions
Showing only changes of commit 5f9ace9bd3 - Show all commits

View File

@ -3,6 +3,7 @@ package postgres
import (
"context"
"errors"
"time"
otterError "git.dragse.it/anthrove/otter-space-sdk/pkg/error"
"git.dragse.it/anthrove/otter-space-sdk/pkg/models"
@ -320,3 +321,101 @@ func GetUserTagWitRelationToFavedPosts(ctx context.Context, db *gorm.DB, anthrov
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
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"testing"
"time"
"git.dragse.it/anthrove/otter-space-sdk/pkg/models"
"git.dragse.it/anthrove/otter-space-sdk/test"
@ -986,3 +987,367 @@ func checkUserSource(got *models.UserSource, want *models.UserSource) bool {
return true
}
func TestUpdateUserSourceScrapeTimeInterval(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
db *gorm.DB
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
scrapeTime models.AnthroveScrapeTimeInterval
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: "",
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "111",
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "",
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 5: scrapeTime is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: validSourceID,
scrapeTime: 0,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := UpdateUserSourceScrapeTimeInterval(tt.args.ctx, tt.args.db, tt.args.anthroveUserID, tt.args.sourceID, tt.args.scrapeTime); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceScrapeTimeInterval() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestUpdateUserSourceLastScrapeTime(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
validScrapeTime := models.AnthroveUserLastScrapeTime(time.Now())
inValidScrapeTime := models.AnthroveUserLastScrapeTime{}
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
db *gorm.DB
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
lastScrapeTime models.AnthroveUserLastScrapeTime
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: "",
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "111",
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "",
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 5: scrapeTime is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: validSourceID,
lastScrapeTime: inValidScrapeTime,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := UpdateUserSourceLastScrapeTime(tt.args.ctx, tt.args.db, tt.args.anthroveUserID, tt.args.sourceID, tt.args.lastScrapeTime); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceLastScrapeTime() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestUpdateUserSourceValidation(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
db *gorm.DB
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
valid bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: validSourceID,
valid: true,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
valid: true,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: "",
sourceID: validSourceID,
valid: true,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "111",
valid: true,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
db: gormDB,
anthroveUserID: validUserID,
sourceID: "",
valid: true,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := UpdateUserSourceValidation(tt.args.ctx, tt.args.db, tt.args.anthroveUserID, tt.args.sourceID, tt.args.valid); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceValidation() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@ -151,18 +151,15 @@ func (p *postgresqlConnection) GetSourceByDomain(ctx context.Context, sourceDoma
// NEW FUNCTIONS
func (p *postgresqlConnection) UpdateUserSourceScrapeTimeInterval(ctx context.Context, anthroveUserID models.AnthroveUserID, sourceID models.AnthroveSourceID, scrapeTime models.AnthroveScrapeTimeInterval) error {
//TODO implement me
panic("implement me")
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 {
//TODO implement me
panic("implement me")
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 {
//TODO implement me
panic("implement me")
return postgres.UpdateUserSourceValidation(ctx, p.db, anthroveUserID, sourceID, valid)
}
func (p *postgresqlConnection) CreateTagAlias(ctx context.Context, tagAliasName models.AnthroveTagAliasName, tagID models.AnthroveTagID) error {

View File

@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"testing"
"time"
"git.dragse.it/anthrove/otter-space-sdk/internal/postgres"
"git.dragse.it/anthrove/otter-space-sdk/pkg/models"
@ -2716,3 +2717,359 @@ func Test_postgresqlConnection_DeleteTagGroup(t *testing.T) {
})
}
}
func Test_postgresqlConnection_UpdateUserSourceScrapeTimeInterval(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = postgres.CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = postgres.CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
scrapeTime models.AnthroveScrapeTimeInterval
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: "",
sourceID: validSourceID,
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "111",
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "",
scrapeTime: 10,
},
wantErr: true,
},
{
name: "Test 5: scrapeTime is empty",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: validSourceID,
scrapeTime: 0,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &postgresqlConnection{
db: gormDB,
debug: true,
}
if err := p.UpdateUserSourceScrapeTimeInterval(tt.args.ctx, tt.args.anthroveUserID, tt.args.sourceID, tt.args.scrapeTime); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceScrapeTimeInterval() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_postgresqlConnection_UpdateUserSourceLastScrapeTime(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
validScrapeTime := models.AnthroveUserLastScrapeTime(time.Now())
inValidScrapeTime := models.AnthroveUserLastScrapeTime{}
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = postgres.CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = postgres.CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
lastScrapeTime models.AnthroveUserLastScrapeTime
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: "",
sourceID: validSourceID,
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "111",
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "",
lastScrapeTime: validScrapeTime,
},
wantErr: true,
},
{
name: "Test 5: scrapeTime is empty",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: validSourceID,
lastScrapeTime: inValidScrapeTime,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &postgresqlConnection{
db: gormDB,
debug: true,
}
if err := p.UpdateUserSourceLastScrapeTime(tt.args.ctx, tt.args.anthroveUserID, tt.args.sourceID, tt.args.lastScrapeTime); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceLastScrapeTime() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_postgresqlConnection_UpdateUserSourceValidation(t *testing.T) {
// Setup trow away container
ctx := context.Background()
container, gormDB, err := test.StartPostgresContainer(ctx)
if err != nil {
t.Fatalf("Could not start PostgreSQL container: %v", err)
}
defer container.Terminate(ctx)
// Setup Test
validUserID := models.AnthroveUserID(fmt.Sprintf("%025s", "User1"))
invalidUserID := models.AnthroveUserID("XXX")
validSourceID := models.AnthroveSourceID(fmt.Sprintf("%025s", "Source1"))
source := &models.Source{
BaseModel: models.BaseModel[models.AnthroveSourceID]{
ID: validSourceID,
},
DisplayName: "e621",
Domain: "e621.net",
Icon: "https://e621.icon",
}
err = postgres.CreateSource(ctx, gormDB, source)
if err != nil {
t.Fatal(err)
}
err = postgres.CreateUserWithRelationToSource(ctx, gormDB, validUserID, validSourceID, "e66e6e6e6", "euser")
if err != nil {
t.Fatal(err)
}
// Test
type args struct {
ctx context.Context
anthroveUserID models.AnthroveUserID
sourceID models.AnthroveSourceID
valid bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test 1: Valid Data",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: validSourceID,
valid: true,
},
wantErr: false,
},
{
name: "Test 2: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: invalidUserID,
sourceID: validSourceID,
valid: true,
},
wantErr: true,
},
{
name: "Test 3: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: "",
sourceID: validSourceID,
valid: true,
},
wantErr: true,
},
{
name: "Test 4: anthroveUserID to short",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "111",
valid: true,
},
wantErr: true,
},
{
name: "Test 5: anthroveUserID is empty",
args: args{
ctx: ctx,
anthroveUserID: validUserID,
sourceID: "",
valid: true,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &postgresqlConnection{
db: gormDB,
debug: true,
}
if err := p.UpdateUserSourceValidation(tt.args.ctx, tt.args.anthroveUserID, tt.args.sourceID, tt.args.valid); (err != nil) != tt.wantErr {
t.Errorf("UpdateUserSourceValidation() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@ -5,6 +5,8 @@ import "fmt"
const (
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"
)
type EntityValidationFailed struct {

View File

@ -1,5 +1,7 @@
package models
import "time"
type AnthroveUserID string
type AnthrovePostID string
type AnthroveSourceID string
@ -9,7 +11,7 @@ type AnthroveTagGroupName string
type AnthroveTagAliasName string
type AnthroveTagID string
type AnthroveScrapeTimeInterval int
type AnthroveUserLastScrapeTime int
type AnthroveUserLastScrapeTime time.Time
type Rating string
type TagType string