Implement list projects
Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
This commit is contained in:
@@ -69,6 +69,14 @@ tasks:
|
||||
deps:
|
||||
- migrate
|
||||
|
||||
down-migrations:
|
||||
desc: Roll back all migrations
|
||||
vars:
|
||||
SOFTPLAYER_DB_CONNECTION_STRING: postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable
|
||||
cmd: "{{ .MIGRATE }} -path=./migrations -database={{ .SOFTPLAYER_DB_CONNECTION_STRING }} down -all"
|
||||
deps:
|
||||
- migrate
|
||||
|
||||
drop-migrations:
|
||||
desc: Drop migrations
|
||||
vars:
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
@@ -54,6 +53,7 @@ func CreateProject(ctx context.Context, db *sql.DB, data *ProjectData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// When a project is created, we need to insert the default owner project membership
|
||||
queryMembership := `
|
||||
INSERT INTO project_membership
|
||||
(project_uuid, user_uuid, role, status, invited_by, joined_at)
|
||||
@@ -104,7 +104,6 @@ func GetProjectByID(ctx context.Context, db *sql.DB, projectID string) (*Project
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(data)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -125,12 +124,46 @@ func UpdateProject(ctx context.Context, db *sql.DB, data *ProjectData) error {
|
||||
}
|
||||
|
||||
// ListProjects get all projects that are available for the user from the database
|
||||
func ListProjects(ctx context.Context, db *sql.DB) ([]*ProjectData, error) {
|
||||
func ListProjects(ctx context.Context, db *sql.DB, userID string) ([]*ProjectData, error) {
|
||||
query := `
|
||||
SELECT p.uuid, p.name
|
||||
FROM projects p
|
||||
JOIN project_membership pm ON pm.project_id = p.id
|
||||
WHERE pm.user_id = ?`
|
||||
fmt.Println(query)
|
||||
return nil, nil
|
||||
JOIN project_membership pm ON pm.project_uuid = p.uuid
|
||||
WHERE pm.user_uuid = $1`
|
||||
rows, err := db.QueryContext(ctx, query, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
result := []*ProjectData{}
|
||||
|
||||
for rows.Next() {
|
||||
pd := &ProjectData{}
|
||||
err := rows.Scan(&pd.UUID, &pd.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, pd)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetProjectOwner should return an owner if a project
|
||||
func GetProjectOwner(ctx context.Context, db *sql.DB, projectID string) (userID string, err error) {
|
||||
query := `
|
||||
SELECT user_uuid
|
||||
FROM project_membership
|
||||
WHERE project_uuid = $1 AND role = 'owner'`
|
||||
|
||||
if err := db.QueryRowContext(ctx, query, projectID).Scan(
|
||||
&userID,
|
||||
); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", ErrNotFound
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -10,8 +12,35 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func newTestUniqueEmail(prefix string) string {
|
||||
if prefix == "" {
|
||||
prefix = "test"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s-%d-%s@example.com",
|
||||
prefix,
|
||||
time.Now().UnixMilli(),
|
||||
uuid.NewString(),
|
||||
)
|
||||
}
|
||||
|
||||
func accountForProject(ctx context.Context) (string, error) {
|
||||
account := &repository.AccountData{
|
||||
UUID: uuid.NewString(),
|
||||
Email: newTestUniqueEmail("projects"),
|
||||
PasswordHash: "dummy",
|
||||
Name: "John",
|
||||
Surname: "Doe",
|
||||
}
|
||||
if err := repository.CreateAccount(ctx, newTestDBConnection(ctx), account); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return account.UUID, nil
|
||||
}
|
||||
func TestIntegrationProjectsCreate_Success(t *testing.T) {
|
||||
createdBy := uuid.NewString()
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-1",
|
||||
@@ -29,7 +58,8 @@ func TestIntegrationProjectsCreate_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntegrationProjectsCreate_CheckFailed(t *testing.T) {
|
||||
createdBy := uuid.NewString()
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-2",
|
||||
@@ -46,7 +76,8 @@ func TestIntegrationProjectsCreate_CheckFailed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntegrationProjectsCreate_AlreadyExistsFail(t *testing.T) {
|
||||
createdBy := uuid.NewString()
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-3",
|
||||
@@ -65,7 +96,8 @@ func TestIntegrationProjectsCreate_AlreadyExistsFail(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntegrationProjectsCreateAndGet_Success(t *testing.T) {
|
||||
createdBy := uuid.NewString()
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-4",
|
||||
@@ -96,7 +128,8 @@ func TestIntegrationProjectsCreateAndGet_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntegrationProjectsCreateUpdateAndGet_Success(t *testing.T) {
|
||||
createdBy := uuid.NewString()
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-5",
|
||||
@@ -131,3 +164,83 @@ func TestIntegrationProjectsCreateUpdateAndGet_Success(t *testing.T) {
|
||||
assert.Equal(t, project.Slug, data.Slug)
|
||||
assert.Equal(t, project.Description, data.Description)
|
||||
}
|
||||
|
||||
func TestIntegrationProjectsCreateAndGetOwner_Success(t *testing.T) {
|
||||
createdBy, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "test-6",
|
||||
Slug: uuid.NewString(),
|
||||
Description: "Test Project Number 1",
|
||||
CreatedBy: createdBy,
|
||||
CreatedAt: time.Now(),
|
||||
ClosedAt: sql.NullTime{Time: time.Now()},
|
||||
Blocked: false,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: createdBy,
|
||||
}
|
||||
|
||||
assert.NoError(t, repository.CreateProject(t.Context(), newTestDBConnection(t.Context()), project))
|
||||
userID, err := repository.GetProjectOwner(t.Context(), newTestDBConnection(t.Context()), project.UUID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, project.CreatedBy, userID)
|
||||
}
|
||||
|
||||
func TestIntegrationListProjectsWorkflow(t *testing.T) {
|
||||
createdBy1, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project1 := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "List 1",
|
||||
Slug: uuid.NewString(),
|
||||
Description: "Test Project Number 1",
|
||||
CreatedBy: createdBy1,
|
||||
CreatedAt: time.Now(),
|
||||
ClosedAt: sql.NullTime{Time: time.Now()},
|
||||
Blocked: false,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: createdBy1,
|
||||
}
|
||||
|
||||
assert.NoError(t, repository.CreateProject(t.Context(), newTestDBConnection(t.Context()), project1))
|
||||
|
||||
project2 := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "List 2",
|
||||
Slug: uuid.NewString(),
|
||||
Description: "Test Project Number 1",
|
||||
CreatedBy: createdBy1,
|
||||
CreatedAt: time.Now(),
|
||||
ClosedAt: sql.NullTime{Time: time.Now()},
|
||||
Blocked: false,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: createdBy1,
|
||||
}
|
||||
assert.NoError(t, repository.CreateProject(t.Context(), newTestDBConnection(t.Context()), project2))
|
||||
|
||||
createdBy2, err := accountForProject(t.Context())
|
||||
assert.NoError(t, err)
|
||||
project3 := &repository.ProjectData{
|
||||
UUID: uuid.NewString(),
|
||||
Name: "List 3",
|
||||
Slug: uuid.NewString(),
|
||||
Description: "Test Project Number 1",
|
||||
CreatedBy: createdBy2,
|
||||
CreatedAt: time.Now(),
|
||||
ClosedAt: sql.NullTime{Time: time.Now()},
|
||||
Blocked: false,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: createdBy2,
|
||||
}
|
||||
|
||||
assert.NoError(t, repository.CreateProject(t.Context(), newTestDBConnection(t.Context()), project3))
|
||||
|
||||
projects, err := repository.ListProjects(t.Context(), newTestDBConnection(t.Context()), createdBy1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, projects, 2)
|
||||
|
||||
projects, err = repository.ListProjects(t.Context(), newTestDBConnection(t.Context()), createdBy2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, projects, 1)
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
DROP TABLE IF EXIST prohects;
|
||||
DROP TABLE projects;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
DROP INDEX IF EXISTS idx_project_membership_project;
|
||||
DROP INDEX IF EXISTS idx_project_membership_user;
|
||||
DROP INDEX idx_project_membership_project;
|
||||
DROP INDEX idx_project_membership_user;
|
||||
|
||||
DROP TABLE IF EXISTS project_membership;
|
||||
DROP TABLE project_membership;
|
||||
|
||||
DROP TYPE IF EXISTS membership_status;
|
||||
DROP TYPE IF EXISTS project_role;
|
||||
DROP TYPE membership_status CASCADE;
|
||||
DROP TYPE project_role CASCADE;
|
||||
|
||||
Reference in New Issue
Block a user