Compare commits
1 Commits
add-actor-model
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0308c70061 |
+13
-13
@@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
v1 "git.tipsy.codes/charles/webstory/pkg/api/webstory/v1"
|
v1 "git.tipsy.codes/charles/webstory/pkg/api/webstory/v1"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/lib/pq"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -92,14 +91,14 @@ func (t *ActorTranslator) ToAPI(dbActor *DBActor) *v1.Actor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &v1.Actor{
|
return &v1.Actor{
|
||||||
Name: name,
|
Name: name,
|
||||||
ActorId: dbActor.ActorID,
|
ActorId: dbActor.ActorID,
|
||||||
NameValue: dbActor.NameValue,
|
NameValue: dbActor.NameValue,
|
||||||
Role: dbActor.Role,
|
Role: dbActor.Role,
|
||||||
Notes: dbActor.Notes,
|
Notes: dbActor.Notes,
|
||||||
CreateTime: timestamppb.New(dbActor.CreatedAt),
|
CreateTime: timestamppb.New(dbActor.CreatedAt),
|
||||||
UpdateTime: timestamppb.New(dbActor.UpdatedAt),
|
UpdateTime: timestamppb.New(dbActor.UpdatedAt),
|
||||||
Etag: dbActor.Etag,
|
Etag: dbActor.Etag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,8 +179,8 @@ func (a *DBActor) insert(db *sql.DB) error {
|
|||||||
a.StoryID,
|
a.StoryID,
|
||||||
a.ActorID,
|
a.ActorID,
|
||||||
a.NameValue,
|
a.NameValue,
|
||||||
pq.Array([]string{a.Role}),
|
a.Role,
|
||||||
pq.Array([]string{a.Notes}),
|
a.Notes,
|
||||||
a.CreatedAt,
|
a.CreatedAt,
|
||||||
a.UpdatedAt,
|
a.UpdatedAt,
|
||||||
etag,
|
etag,
|
||||||
@@ -396,11 +395,12 @@ func GetActorIDForResourceName(resourceName string) (int64, string, error) {
|
|||||||
remaining := strings.TrimPrefix(resourceName, prefix)
|
remaining := strings.TrimPrefix(resourceName, prefix)
|
||||||
|
|
||||||
actorPrefix := "actors/"
|
actorPrefix := "actors/"
|
||||||
if !strings.HasPrefix(remaining, actorPrefix) {
|
idx := strings.Index(remaining, actorPrefix)
|
||||||
|
if idx == -1 {
|
||||||
return 0, "", fmt.Errorf("invalid actor resource name: must contain '%s', got '%s'", actorPrefix, remaining)
|
return 0, "", fmt.Errorf("invalid actor resource name: must contain '%s', got '%s'", actorPrefix, remaining)
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID := strings.TrimPrefix(remaining, actorPrefix)
|
actorID := remaining[idx+len(actorPrefix):]
|
||||||
if actorID == "" {
|
if actorID == "" {
|
||||||
return 0, "", errors.New("actor_id is empty in resource name")
|
return 0, "", errors.New("actor_id is empty in resource name")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
|
|
||||||
v1 "git.tipsy.codes/charles/webstory/pkg/api/webstory/v1"
|
v1 "git.tipsy.codes/charles/webstory/pkg/api/webstory/v1"
|
||||||
"git.tipsy.codes/charles/webstory/pkg/database/schema"
|
"git.tipsy.codes/charles/webstory/pkg/database/schema"
|
||||||
|
|
||||||
|
story "git.tipsy.codes/charles/webstory/pkg/database/story"
|
||||||
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
|
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
@@ -105,8 +107,8 @@ func TestActorTranslator_ToAPI(t *testing.T) {
|
|||||||
checkFunc func(*v1.Actor) error
|
checkFunc func(*v1.Actor) error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nil dbActor returns nil",
|
name: "nil dbActor returns nil",
|
||||||
dbActor: nil,
|
dbActor: nil,
|
||||||
checkFunc: func(a *v1.Actor) error {
|
checkFunc: func(a *v1.Actor) error {
|
||||||
if a != nil {
|
if a != nil {
|
||||||
return fmt.Errorf("expected nil, got %v", a)
|
return fmt.Errorf("expected nil, got %v", a)
|
||||||
@@ -222,9 +224,19 @@ func TestDBActor_Save(t *testing.T) {
|
|||||||
t.Fatalf("failed to run schema: %v", err)
|
t.Fatalf("failed to run schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a story first
|
||||||
|
story := &story.DBStory{
|
||||||
|
StoryID: "save-test-story-1",
|
||||||
|
Title: "Save Test Story",
|
||||||
|
}
|
||||||
|
err = story.Save(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save story: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actor := &DBActor{
|
actor := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "save-test1",
|
ActorID: "save-test-1",
|
||||||
NameValue: "Save Test",
|
NameValue: "Save Test",
|
||||||
Role: "Test Role",
|
Role: "Test Role",
|
||||||
Notes: "Test notes",
|
Notes: "Test notes",
|
||||||
@@ -291,9 +303,19 @@ func TestDBActor_GetByID(t *testing.T) {
|
|||||||
t.Fatalf("failed to run schema: %v", err)
|
t.Fatalf("failed to run schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a story first
|
||||||
|
story := &story.DBStory{
|
||||||
|
StoryID: "get-by-id-story-1",
|
||||||
|
Title: "Get By ID Story",
|
||||||
|
}
|
||||||
|
err = story.Save(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save story: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actor := &DBActor{
|
actor := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "get-by-id-test",
|
ActorID: "get-by-id-test-1",
|
||||||
NameValue: "Get Test",
|
NameValue: "Get Test",
|
||||||
}
|
}
|
||||||
err = actor.Save(db)
|
err = actor.Save(db)
|
||||||
@@ -340,9 +362,19 @@ func TestDBActor_GetByActorID(t *testing.T) {
|
|||||||
t.Fatalf("failed to run schema: %v", err)
|
t.Fatalf("failed to run schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a story first
|
||||||
|
story := &story.DBStory{
|
||||||
|
StoryID: "get-by-actor-id-story-1",
|
||||||
|
Title: "Get By Actor ID Story",
|
||||||
|
}
|
||||||
|
err = story.Save(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save story: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actor := &DBActor{
|
actor := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "get-by-actor-id-test",
|
ActorID: "get-by-actor-id-test-1",
|
||||||
NameValue: "Get By Actor ID Test",
|
NameValue: "Get By Actor ID Test",
|
||||||
}
|
}
|
||||||
err = actor.Save(db)
|
err = actor.Save(db)
|
||||||
@@ -386,14 +418,24 @@ func TestDBActor_ListByStoryID(t *testing.T) {
|
|||||||
t.Fatalf("failed to run schema: %v", err)
|
t.Fatalf("failed to run schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a story first
|
||||||
|
story := &story.DBStory{
|
||||||
|
StoryID: "list-test-story-1",
|
||||||
|
Title: "List Test Story",
|
||||||
|
}
|
||||||
|
err = story.Save(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save story: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actor1 := &DBActor{
|
actor1 := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "list-test-1",
|
ActorID: "list-test-1",
|
||||||
NameValue: "Actor One",
|
NameValue: "Actor One",
|
||||||
Role: "Role One",
|
Role: "Role One",
|
||||||
}
|
}
|
||||||
actor2 := &DBActor{
|
actor2 := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "list-test-2",
|
ActorID: "list-test-2",
|
||||||
NameValue: "Actor Two",
|
NameValue: "Actor Two",
|
||||||
Role: "Role Two",
|
Role: "Role Two",
|
||||||
@@ -448,9 +490,19 @@ func TestDBActor_Delete(t *testing.T) {
|
|||||||
t.Fatalf("failed to run schema: %v", err)
|
t.Fatalf("failed to run schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a story first
|
||||||
|
story := &story.DBStory{
|
||||||
|
StoryID: "delete-test-story-1",
|
||||||
|
Title: "Delete Test Story",
|
||||||
|
}
|
||||||
|
err = story.Save(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save story: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actor := &DBActor{
|
actor := &DBActor{
|
||||||
StoryID: 1,
|
StoryID: story.ID,
|
||||||
ActorID: "delete-test-1",
|
ActorID: "delete-test-actor-1",
|
||||||
NameValue: "Delete Test",
|
NameValue: "Delete Test",
|
||||||
}
|
}
|
||||||
err = actor.Save(db)
|
err = actor.Save(db)
|
||||||
@@ -492,74 +544,74 @@ func TestDBActor_GetActorIDForResourceName(t *testing.T) {
|
|||||||
expectErr bool
|
expectErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid story/actor resource name",
|
name: "valid story/actor resource name",
|
||||||
input: "stories/1/actors/my-actor",
|
input: "stories/1/actors/my-actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "my-actor",
|
actorID: "my-actor",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid story/actor resource name with numbers",
|
name: "valid story/actor resource name with numbers",
|
||||||
input: "stories/100/actors/actor-123",
|
input: "stories/100/actors/actor-123",
|
||||||
storyID: 100,
|
storyID: 100,
|
||||||
actorID: "actor-123",
|
actorID: "actor-123",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing actors/ prefix",
|
name: "missing actors/ prefix",
|
||||||
input: "stories/1/actor/my-actor",
|
input: "stories/1/actor/my-actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actor_id with underscore not allowed",
|
name: "actor_id with underscore not allowed",
|
||||||
input: "stories/1/actors/my_actor",
|
input: "stories/1/actors/my_actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actor_id with spaces not allowed",
|
name: "actor_id with spaces not allowed",
|
||||||
input: "stories/1/actors/my actor",
|
input: "stories/1/actors/my actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actor_id with uppercase not allowed",
|
name: "actor_id with uppercase not allowed",
|
||||||
input: "stories/1/actors/MyActor",
|
input: "stories/1/actors/MyActor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actor_id with dots not allowed",
|
name: "actor_id with dots not allowed",
|
||||||
input: "stories/1/actors/my.actor",
|
input: "stories/1/actors/my.actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing stories/ prefix",
|
name: "missing stories/ prefix",
|
||||||
input: "actors/my-actor",
|
input: "actors/my-actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty actor_id",
|
name: "empty actor_id",
|
||||||
input: "stories/1/actors/",
|
input: "stories/1/actors/",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong stories/ prefix",
|
name: "wrong stories/ prefix",
|
||||||
input: "story/1/actors/my-actor",
|
input: "story/1/actors/my-actor",
|
||||||
storyID: 1,
|
storyID: 1,
|
||||||
actorID: "",
|
actorID: "",
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS actors (
|
|||||||
-- Constraints
|
-- Constraints
|
||||||
CONSTRAINT actors_story_actor_id_unique UNIQUE (story_id, actor_id),
|
CONSTRAINT actors_story_actor_id_unique UNIQUE (story_id, actor_id),
|
||||||
CONSTRAINT actors_story_id_exists CHECK (story_id > 0),
|
CONSTRAINT actors_story_id_exists CHECK (story_id > 0),
|
||||||
CONSTRAINT actors_actor_id_pattern CHECK (actor_id ~ '^[a-z][0-9-]{2,61}[0-9]$'),
|
CONSTRAINT actors_actor_id_pattern CHECK (actor_id ~ '^[a-z][0-9a-z-]{2,61}[0-9a-z]$'),
|
||||||
CONSTRAINT actors_etag_pattern CHECK (char_length(etag) > 0)
|
CONSTRAINT actors_etag_pattern CHECK (char_length(etag) > 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user