cleanup: refactor api architecture & add user roles

This commit is contained in:
Jordan Knott
2020-07-04 18:02:57 -05:00
parent a3958595cd
commit eaffaa70df
141 changed files with 12487 additions and 3792 deletions

105
api/internal/auth/auth.go Normal file
View File

@ -0,0 +1,105 @@
package auth
import (
"time"
"github.com/dgrijalva/jwt-go"
log "github.com/sirupsen/logrus"
)
var jwtKey = []byte("citadel_test_key")
type AccessTokenClaims struct {
UserID string `json:"userId"`
jwt.StandardClaims
}
type RefreshTokenClaims struct {
UserID string `json:"userId"`
jwt.StandardClaims
}
type ErrExpiredToken struct{}
func (r *ErrExpiredToken) Error() string {
return "token is expired"
}
type ErrMalformedToken struct{}
func (r *ErrMalformedToken) Error() string {
return "token is malformed"
}
func NewAccessToken(userID string) (string, error) {
accessExpirationTime := time.Now().Add(5 * time.Second)
accessClaims := &AccessTokenClaims{
UserID: userID,
StandardClaims: jwt.StandardClaims{ExpiresAt: accessExpirationTime.Unix()},
}
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
accessTokenString, err := accessToken.SignedString(jwtKey)
if err != nil {
return "", err
}
return accessTokenString, nil
}
func NewAccessTokenCustomExpiration(userID string, dur time.Duration) (string, error) {
accessExpirationTime := time.Now().Add(dur)
accessClaims := &AccessTokenClaims{
UserID: userID,
StandardClaims: jwt.StandardClaims{ExpiresAt: accessExpirationTime.Unix()},
}
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
accessTokenString, err := accessToken.SignedString(jwtKey)
if err != nil {
return "", err
}
return accessTokenString, nil
}
func ValidateAccessToken(accessTokenString string) (AccessTokenClaims, error) {
accessClaims := &AccessTokenClaims{}
accessToken, err := jwt.ParseWithClaims(accessTokenString, accessClaims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
return *accessClaims, nil
}
if accessToken.Valid {
log.WithFields(log.Fields{
"token": accessTokenString,
"timeToExpire": time.Unix(accessClaims.ExpiresAt, 0),
}).Debug("token is valid")
return *accessClaims, nil
}
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return AccessTokenClaims{}, &ErrMalformedToken{}
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
return AccessTokenClaims{}, &ErrExpiredToken{}
}
}
return AccessTokenClaims{}, err
}
func NewRefreshToken(userID string) (string, time.Time, error) {
refreshExpirationTime := time.Now().Add(24 * time.Hour)
refreshClaims := &RefreshTokenClaims{
UserID: userID,
StandardClaims: jwt.StandardClaims{ExpiresAt: refreshExpirationTime.Unix()},
}
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
refreshTokenString, err := refreshToken.SignedString(jwtKey)
if err != nil {
return "", time.Time{}, err
}
return refreshTokenString, refreshExpirationTime, nil
}

29
api/internal/db/db.go Normal file
View File

@ -0,0 +1,29 @@
// Code generated by sqlc. DO NOT EDIT.
package db
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@ -0,0 +1,81 @@
// Code generated by sqlc. DO NOT EDIT.
// source: label_color.sql
package db
import (
"context"
"github.com/google/uuid"
)
const createLabelColor = `-- name: CreateLabelColor :one
INSERT INTO label_color (name, color_hex, position) VALUES ($1, $2, $3)
RETURNING label_color_id, color_hex, position, name
`
type CreateLabelColorParams struct {
Name string `json:"name"`
ColorHex string `json:"color_hex"`
Position float64 `json:"position"`
}
func (q *Queries) CreateLabelColor(ctx context.Context, arg CreateLabelColorParams) (LabelColor, error) {
row := q.db.QueryRowContext(ctx, createLabelColor, arg.Name, arg.ColorHex, arg.Position)
var i LabelColor
err := row.Scan(
&i.LabelColorID,
&i.ColorHex,
&i.Position,
&i.Name,
)
return i, err
}
const getLabelColorByID = `-- name: GetLabelColorByID :one
SELECT label_color_id, color_hex, position, name FROM label_color WHERE label_color_id = $1
`
func (q *Queries) GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error) {
row := q.db.QueryRowContext(ctx, getLabelColorByID, labelColorID)
var i LabelColor
err := row.Scan(
&i.LabelColorID,
&i.ColorHex,
&i.Position,
&i.Name,
)
return i, err
}
const getLabelColors = `-- name: GetLabelColors :many
SELECT label_color_id, color_hex, position, name FROM label_color
`
func (q *Queries) GetLabelColors(ctx context.Context) ([]LabelColor, error) {
rows, err := q.db.QueryContext(ctx, getLabelColors)
if err != nil {
return nil, err
}
defer rows.Close()
var items []LabelColor
for rows.Next() {
var i LabelColor
if err := rows.Scan(
&i.LabelColorID,
&i.ColorHex,
&i.Position,
&i.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

139
api/internal/db/models.go Normal file
View File

@ -0,0 +1,139 @@
// Code generated by sqlc. DO NOT EDIT.
package db
import (
"database/sql"
"time"
"github.com/google/uuid"
)
type LabelColor struct {
LabelColorID uuid.UUID `json:"label_color_id"`
ColorHex string `json:"color_hex"`
Position float64 `json:"position"`
Name string `json:"name"`
}
type Organization struct {
OrganizationID uuid.UUID `json:"organization_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
}
type Project struct {
ProjectID uuid.UUID `json:"project_id"`
TeamID uuid.UUID `json:"team_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Owner uuid.UUID `json:"owner"`
}
type ProjectLabel struct {
ProjectLabelID uuid.UUID `json:"project_label_id"`
ProjectID uuid.UUID `json:"project_id"`
LabelColorID uuid.UUID `json:"label_color_id"`
CreatedDate time.Time `json:"created_date"`
Name sql.NullString `json:"name"`
}
type ProjectMember struct {
ProjectMemberID uuid.UUID `json:"project_member_id"`
ProjectID uuid.UUID `json:"project_id"`
UserID uuid.UUID `json:"user_id"`
AddedAt time.Time `json:"added_at"`
RoleCode string `json:"role_code"`
}
type RefreshToken struct {
TokenID uuid.UUID `json:"token_id"`
UserID uuid.UUID `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
type Role struct {
Code string `json:"code"`
Name string `json:"name"`
}
type Task struct {
TaskID uuid.UUID `json:"task_id"`
TaskGroupID uuid.UUID `json:"task_group_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
Description sql.NullString `json:"description"`
DueDate sql.NullTime `json:"due_date"`
Complete bool `json:"complete"`
}
type TaskAssigned struct {
TaskAssignedID uuid.UUID `json:"task_assigned_id"`
TaskID uuid.UUID `json:"task_id"`
UserID uuid.UUID `json:"user_id"`
AssignedDate time.Time `json:"assigned_date"`
}
type TaskChecklist struct {
TaskChecklistID uuid.UUID `json:"task_checklist_id"`
TaskID uuid.UUID `json:"task_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type TaskChecklistItem struct {
TaskChecklistItemID uuid.UUID `json:"task_checklist_item_id"`
TaskChecklistID uuid.UUID `json:"task_checklist_id"`
CreatedAt time.Time `json:"created_at"`
Complete bool `json:"complete"`
Name string `json:"name"`
Position float64 `json:"position"`
DueDate sql.NullTime `json:"due_date"`
}
type TaskGroup struct {
TaskGroupID uuid.UUID `json:"task_group_id"`
ProjectID uuid.UUID `json:"project_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type TaskLabel struct {
TaskLabelID uuid.UUID `json:"task_label_id"`
TaskID uuid.UUID `json:"task_id"`
ProjectLabelID uuid.UUID `json:"project_label_id"`
AssignedDate time.Time `json:"assigned_date"`
}
type Team struct {
TeamID uuid.UUID `json:"team_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
OrganizationID uuid.UUID `json:"organization_id"`
Owner uuid.UUID `json:"owner"`
}
type TeamMember struct {
TeamMemberID uuid.UUID `json:"team_member_id"`
TeamID uuid.UUID `json:"team_id"`
UserID uuid.UUID `json:"user_id"`
Addeddate time.Time `json:"addeddate"`
RoleCode string `json:"role_code"`
}
type UserAccount struct {
UserID uuid.UUID `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
Email string `json:"email"`
Username string `json:"username"`
PasswordHash string `json:"password_hash"`
ProfileBgColor string `json:"profile_bg_color"`
FullName string `json:"full_name"`
Initials string `json:"initials"`
ProfileAvatarUrl sql.NullString `json:"profile_avatar_url"`
RoleCode string `json:"role_code"`
}

View File

@ -0,0 +1,52 @@
// Code generated by sqlc. DO NOT EDIT.
// source: organization.sql
package db
import (
"context"
"time"
)
const createOrganization = `-- name: CreateOrganization :one
INSERT INTO organization (created_at, name) VALUES ($1, $2) RETURNING organization_id, created_at, name
`
type CreateOrganizationParams struct {
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
}
func (q *Queries) CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error) {
row := q.db.QueryRowContext(ctx, createOrganization, arg.CreatedAt, arg.Name)
var i Organization
err := row.Scan(&i.OrganizationID, &i.CreatedAt, &i.Name)
return i, err
}
const getAllOrganizations = `-- name: GetAllOrganizations :many
SELECT organization_id, created_at, name FROM organization
`
func (q *Queries) GetAllOrganizations(ctx context.Context) ([]Organization, error) {
rows, err := q.db.QueryContext(ctx, getAllOrganizations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Organization
for rows.Next() {
var i Organization
if err := rows.Scan(&i.OrganizationID, &i.CreatedAt, &i.Name); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -0,0 +1,294 @@
// Code generated by sqlc. DO NOT EDIT.
// source: project.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createProject = `-- name: CreateProject :one
INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RETURNING project_id, team_id, created_at, name, owner
`
type CreateProjectParams struct {
Owner uuid.UUID `json:"owner"`
TeamID uuid.UUID `json:"team_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
}
func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) {
row := q.db.QueryRowContext(ctx, createProject,
arg.Owner,
arg.TeamID,
arg.CreatedAt,
arg.Name,
)
var i Project
err := row.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
)
return i, err
}
const createProjectMember = `-- name: CreateProjectMember :one
INSERT INTO project_member (project_id, user_id, role_code, added_at) VALUES ($1, $2, $3, $4)
RETURNING project_member_id, project_id, user_id, added_at, role_code
`
type CreateProjectMemberParams struct {
ProjectID uuid.UUID `json:"project_id"`
UserID uuid.UUID `json:"user_id"`
RoleCode string `json:"role_code"`
AddedAt time.Time `json:"added_at"`
}
func (q *Queries) CreateProjectMember(ctx context.Context, arg CreateProjectMemberParams) (ProjectMember, error) {
row := q.db.QueryRowContext(ctx, createProjectMember,
arg.ProjectID,
arg.UserID,
arg.RoleCode,
arg.AddedAt,
)
var i ProjectMember
err := row.Scan(
&i.ProjectMemberID,
&i.ProjectID,
&i.UserID,
&i.AddedAt,
&i.RoleCode,
)
return i, err
}
const deleteProjectByID = `-- name: DeleteProjectByID :exec
DELETE FROM project WHERE project_id = $1
`
func (q *Queries) DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteProjectByID, projectID)
return err
}
const deleteProjectMember = `-- name: DeleteProjectMember :exec
DELETE FROM project_member WHERE user_id = $1 AND project_id = $2
`
type DeleteProjectMemberParams struct {
UserID uuid.UUID `json:"user_id"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) DeleteProjectMember(ctx context.Context, arg DeleteProjectMemberParams) error {
_, err := q.db.ExecContext(ctx, deleteProjectMember, arg.UserID, arg.ProjectID)
return err
}
const getAllProjects = `-- name: GetAllProjects :many
SELECT project_id, team_id, created_at, name, owner FROM project
`
func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
rows, err := q.db.QueryContext(ctx, getAllProjects)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Project
for rows.Next() {
var i Project
if err := rows.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many
SELECT project_id, team_id, created_at, name, owner FROM project WHERE team_id = $1
`
func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) {
rows, err := q.db.QueryContext(ctx, getAllProjectsForTeam, teamID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Project
for rows.Next() {
var i Project
if err := rows.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProjectByID = `-- name: GetProjectByID :one
SELECT project_id, team_id, created_at, name, owner FROM project WHERE project_id = $1
`
func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) {
row := q.db.QueryRowContext(ctx, getProjectByID, projectID)
var i Project
err := row.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
)
return i, err
}
const getProjectMembersForProjectID = `-- name: GetProjectMembersForProjectID :many
SELECT project_member_id, project_id, user_id, added_at, role_code FROM project_member WHERE project_id = $1
`
func (q *Queries) GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error) {
rows, err := q.db.QueryContext(ctx, getProjectMembersForProjectID, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProjectMember
for rows.Next() {
var i ProjectMember
if err := rows.Scan(
&i.ProjectMemberID,
&i.ProjectID,
&i.UserID,
&i.AddedAt,
&i.RoleCode,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getRoleForProjectMemberByUserID = `-- name: GetRoleForProjectMemberByUserID :one
SELECT code, role.name FROM project_member INNER JOIN role ON role.code = project_member.role_code
WHERE user_id = $1 AND project_id = $2
`
type GetRoleForProjectMemberByUserIDParams struct {
UserID uuid.UUID `json:"user_id"`
ProjectID uuid.UUID `json:"project_id"`
}
func (q *Queries) GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error) {
row := q.db.QueryRowContext(ctx, getRoleForProjectMemberByUserID, arg.UserID, arg.ProjectID)
var i Role
err := row.Scan(&i.Code, &i.Name)
return i, err
}
const setProjectOwner = `-- name: SetProjectOwner :one
UPDATE project SET owner = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, owner
`
type SetProjectOwnerParams struct {
ProjectID uuid.UUID `json:"project_id"`
Owner uuid.UUID `json:"owner"`
}
func (q *Queries) SetProjectOwner(ctx context.Context, arg SetProjectOwnerParams) (Project, error) {
row := q.db.QueryRowContext(ctx, setProjectOwner, arg.ProjectID, arg.Owner)
var i Project
err := row.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
)
return i, err
}
const updateProjectMemberRole = `-- name: UpdateProjectMemberRole :one
UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2
RETURNING project_member_id, project_id, user_id, added_at, role_code
`
type UpdateProjectMemberRoleParams struct {
ProjectID uuid.UUID `json:"project_id"`
UserID uuid.UUID `json:"user_id"`
RoleCode string `json:"role_code"`
}
func (q *Queries) UpdateProjectMemberRole(ctx context.Context, arg UpdateProjectMemberRoleParams) (ProjectMember, error) {
row := q.db.QueryRowContext(ctx, updateProjectMemberRole, arg.ProjectID, arg.UserID, arg.RoleCode)
var i ProjectMember
err := row.Scan(
&i.ProjectMemberID,
&i.ProjectID,
&i.UserID,
&i.AddedAt,
&i.RoleCode,
)
return i, err
}
const updateProjectNameByID = `-- name: UpdateProjectNameByID :one
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, owner
`
type UpdateProjectNameByIDParams struct {
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
}
func (q *Queries) UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error) {
row := q.db.QueryRowContext(ctx, updateProjectNameByID, arg.ProjectID, arg.Name)
var i Project
err := row.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
)
return i, err
}

View File

@ -0,0 +1,168 @@
// Code generated by sqlc. DO NOT EDIT.
// source: project_label.sql
package db
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const createProjectLabel = `-- name: CreateProjectLabel :one
INSERT INTO project_label (project_id, label_color_id, created_date, name)
VALUES ($1, $2, $3, $4) RETURNING project_label_id, project_id, label_color_id, created_date, name
`
type CreateProjectLabelParams struct {
ProjectID uuid.UUID `json:"project_id"`
LabelColorID uuid.UUID `json:"label_color_id"`
CreatedDate time.Time `json:"created_date"`
Name sql.NullString `json:"name"`
}
func (q *Queries) CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error) {
row := q.db.QueryRowContext(ctx, createProjectLabel,
arg.ProjectID,
arg.LabelColorID,
arg.CreatedDate,
arg.Name,
)
var i ProjectLabel
err := row.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
)
return i, err
}
const deleteProjectLabelByID = `-- name: DeleteProjectLabelByID :exec
DELETE FROM project_label WHERE project_label_id = $1
`
func (q *Queries) DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteProjectLabelByID, projectLabelID)
return err
}
const getProjectLabelByID = `-- name: GetProjectLabelByID :one
SELECT project_label_id, project_id, label_color_id, created_date, name FROM project_label WHERE project_label_id = $1
`
func (q *Queries) GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error) {
row := q.db.QueryRowContext(ctx, getProjectLabelByID, projectLabelID)
var i ProjectLabel
err := row.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
)
return i, err
}
const getProjectLabelsForProject = `-- name: GetProjectLabelsForProject :many
SELECT project_label_id, project_id, label_color_id, created_date, name FROM project_label WHERE project_id = $1
`
func (q *Queries) GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error) {
rows, err := q.db.QueryContext(ctx, getProjectLabelsForProject, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProjectLabel
for rows.Next() {
var i ProjectLabel
if err := rows.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateProjectLabel = `-- name: UpdateProjectLabel :one
UPDATE project_label SET name = $2, label_color_id = $3 WHERE project_label_id = $1 RETURNING project_label_id, project_id, label_color_id, created_date, name
`
type UpdateProjectLabelParams struct {
ProjectLabelID uuid.UUID `json:"project_label_id"`
Name sql.NullString `json:"name"`
LabelColorID uuid.UUID `json:"label_color_id"`
}
func (q *Queries) UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error) {
row := q.db.QueryRowContext(ctx, updateProjectLabel, arg.ProjectLabelID, arg.Name, arg.LabelColorID)
var i ProjectLabel
err := row.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
)
return i, err
}
const updateProjectLabelColor = `-- name: UpdateProjectLabelColor :one
UPDATE project_label SET label_color_id = $2 WHERE project_label_id = $1 RETURNING project_label_id, project_id, label_color_id, created_date, name
`
type UpdateProjectLabelColorParams struct {
ProjectLabelID uuid.UUID `json:"project_label_id"`
LabelColorID uuid.UUID `json:"label_color_id"`
}
func (q *Queries) UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error) {
row := q.db.QueryRowContext(ctx, updateProjectLabelColor, arg.ProjectLabelID, arg.LabelColorID)
var i ProjectLabel
err := row.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
)
return i, err
}
const updateProjectLabelName = `-- name: UpdateProjectLabelName :one
UPDATE project_label SET name = $2 WHERE project_label_id = $1 RETURNING project_label_id, project_id, label_color_id, created_date, name
`
type UpdateProjectLabelNameParams struct {
ProjectLabelID uuid.UUID `json:"project_label_id"`
Name sql.NullString `json:"name"`
}
func (q *Queries) UpdateProjectLabelName(ctx context.Context, arg UpdateProjectLabelNameParams) (ProjectLabel, error) {
row := q.db.QueryRowContext(ctx, updateProjectLabelName, arg.ProjectLabelID, arg.Name)
var i ProjectLabel
err := row.Scan(
&i.ProjectLabelID,
&i.ProjectID,
&i.LabelColorID,
&i.CreatedDate,
&i.Name,
)
return i, err
}

101
api/internal/db/querier.go Normal file
View File

@ -0,0 +1,101 @@
// Code generated by sqlc. DO NOT EDIT.
package db
import (
"context"
"github.com/google/uuid"
)
type Querier interface {
CreateLabelColor(ctx context.Context, arg CreateLabelColorParams) (LabelColor, error)
CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error)
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error)
CreateProjectMember(ctx context.Context, arg CreateProjectMemberParams) (ProjectMember, error)
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
CreateTaskChecklist(ctx context.Context, arg CreateTaskChecklistParams) (TaskChecklist, error)
CreateTaskChecklistItem(ctx context.Context, arg CreateTaskChecklistItemParams) (TaskChecklistItem, error)
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
DeleteExpiredTokens(ctx context.Context) error
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error
DeleteProjectMember(ctx context.Context, arg DeleteProjectMemberParams) error
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error)
DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error
DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error
DeleteTaskChecklistItem(ctx context.Context, taskChecklistItemID uuid.UUID) error
DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error
DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error
DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error
GetAllOrganizations(ctx context.Context) ([]Organization, error)
GetAllProjects(ctx context.Context) ([]Project, error)
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error)
GetAllTasks(ctx context.Context) ([]Task, error)
GetAllTeams(ctx context.Context) ([]Team, error)
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
GetLabelColors(ctx context.Context) ([]LabelColor, error)
GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error)
GetProjectIDForTask(ctx context.Context, taskID uuid.UUID) (uuid.UUID, error)
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error)
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error)
GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error)
GetRoleForUserID(ctx context.Context, userID uuid.UUID) (GetRoleForUserIDRow, error)
GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error)
GetTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) (TaskChecklist, error)
GetTaskChecklistItemByID(ctx context.Context, taskChecklistItemID uuid.UUID) (TaskChecklistItem, error)
GetTaskChecklistItemsForTaskChecklist(ctx context.Context, taskChecklistID uuid.UUID) ([]TaskChecklistItem, error)
GetTaskChecklistsForTask(ctx context.Context, taskID uuid.UUID) ([]TaskChecklist, error)
GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error)
GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error)
GetTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) (TaskLabel, error)
GetTaskLabelForTaskByProjectLabelID(ctx context.Context, arg GetTaskLabelForTaskByProjectLabelIDParams) (TaskLabel, error)
GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error)
GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error)
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
GetTeamMemberByID(ctx context.Context, arg GetTeamMemberByIDParams) (TeamMember, error)
GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error)
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
SetProjectOwner(ctx context.Context, arg SetProjectOwnerParams) (Project, error)
SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error)
SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error)
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)
SetTeamOwner(ctx context.Context, arg SetTeamOwnerParams) (Team, error)
UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error)
UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error)
UpdateProjectLabelName(ctx context.Context, arg UpdateProjectLabelNameParams) (ProjectLabel, error)
UpdateProjectMemberRole(ctx context.Context, arg UpdateProjectMemberRoleParams) (ProjectMember, error)
UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error)
UpdateTaskChecklistItemName(ctx context.Context, arg UpdateTaskChecklistItemNameParams) (TaskChecklistItem, error)
UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error)
UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error)
UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error)
UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error)
UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error)
UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error)
UpdateTeamMemberRole(ctx context.Context, arg UpdateTeamMemberRoleParams) (TeamMember, error)
UpdateUserAccountProfileAvatarURL(ctx context.Context, arg UpdateUserAccountProfileAvatarURLParams) (UserAccount, error)
}
var _ Querier = (*Queries)(nil)

View File

@ -0,0 +1,9 @@
-- name: GetLabelColorByID :one
SELECT * FROM label_color WHERE label_color_id = $1;
-- name: GetLabelColors :many
SELECT * FROM label_color;
-- name: CreateLabelColor :one
INSERT INTO label_color (name, color_hex, position) VALUES ($1, $2, $3)
RETURNING *;

View File

@ -0,0 +1,5 @@
-- name: GetAllOrganizations :many
SELECT * FROM organization;
-- name: CreateOrganization :one
INSERT INTO organization (created_at, name) VALUES ($1, $2) RETURNING *;

View File

@ -0,0 +1,41 @@
-- name: GetAllProjects :many
SELECT * FROM project;
-- name: GetAllProjectsForTeam :many
SELECT * FROM project WHERE team_id = $1;
-- name: GetProjectByID :one
SELECT * FROM project WHERE project_id = $1;
-- name: CreateProject :one
INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RETURNING *;
-- name: SetProjectOwner :one
UPDATE project SET owner = $2 WHERE project_id = $1 RETURNING *;
-- name: UpdateProjectNameByID :one
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING *;
-- name: DeleteProjectByID :exec
DELETE FROM project WHERE project_id = $1;
-- name: GetProjectMembersForProjectID :many
SELECT * FROM project_member WHERE project_id = $1;
-- name: GetRoleForProjectMemberByUserID :one
SELECT code, role.name FROM project_member INNER JOIN role ON role.code = project_member.role_code
WHERE user_id = $1 AND project_id = $2;
-- name: CreateProjectMember :one
INSERT INTO project_member (project_id, user_id, role_code, added_at) VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: DeleteProjectMember :exec
DELETE FROM project_member WHERE user_id = $1 AND project_id = $2;
-- name: UpdateProjectMemberRole :one
UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2
RETURNING *;

View File

@ -0,0 +1,21 @@
-- name: CreateProjectLabel :one
INSERT INTO project_label (project_id, label_color_id, created_date, name)
VALUES ($1, $2, $3, $4) RETURNING *;
-- name: GetProjectLabelsForProject :many
SELECT * FROM project_label WHERE project_id = $1;
-- name: GetProjectLabelByID :one
SELECT * FROM project_label WHERE project_label_id = $1;
-- name: DeleteProjectLabelByID :exec
DELETE FROM project_label WHERE project_label_id = $1;
-- name: UpdateProjectLabelName :one
UPDATE project_label SET name = $2 WHERE project_label_id = $1 RETURNING *;
-- name: UpdateProjectLabelColor :one
UPDATE project_label SET label_color_id = $2 WHERE project_label_id = $1 RETURNING *;
-- name: UpdateProjectLabel :one
UPDATE project_label SET name = $2, label_color_id = $3 WHERE project_label_id = $1 RETURNING *;

View File

@ -0,0 +1,38 @@
-- name: CreateTask :one
INSERT INTO task (task_group_id, created_at, name, position)
VALUES($1, $2, $3, $4) RETURNING *;
-- name: UpdateTaskDescription :one
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING *;
-- name: GetTaskByID :one
SELECT * FROM task WHERE task_id = $1;
-- name: GetTasksForTaskGroupID :many
SELECT * FROM task WHERE task_group_id = $1;
-- name: GetAllTasks :many
SELECT * FROM task;
-- name: UpdateTaskLocation :one
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING *;
-- name: DeleteTaskByID :exec
DELETE FROM task WHERE task_id = $1;
-- name: UpdateTaskName :one
UPDATE task SET name = $2 WHERE task_id = $1 RETURNING *;
-- name: DeleteTasksByTaskGroupID :execrows
DELETE FROM task where task_group_id = $1;
-- name: UpdateTaskDueDate :one
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING *;
-- name: SetTaskComplete :one
UPDATE task SET complete = $2 WHERE task_id = $1 RETURNING *;
-- name: GetProjectIDForTask :one
SELECT project_id FROM task
INNER JOIN task_group ON task_group.task_group_id = task.task_group_id
WHERE task_id = $1;

View File

@ -0,0 +1,9 @@
-- name: CreateTaskAssigned :one
INSERT INTO task_assigned (task_id, user_id, assigned_date)
VALUES($1, $2, $3) RETURNING *;
-- name: GetAssignedMembersForTask :many
SELECT * FROM task_assigned WHERE task_id = $1;
-- name: DeleteTaskAssignedByID :one
DELETE FROM task_assigned WHERE task_id = $1 AND user_id = $2 RETURNING *;

View File

@ -0,0 +1,37 @@
-- name: CreateTaskChecklist :one
INSERT INTO task_checklist (task_id, created_at, name, position) VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetTaskChecklistsForTask :many
SELECT * FROM task_checklist WHERE task_id = $1;
-- name: UpdateTaskChecklistName :one
UPDATE task_checklist SET name = $2 WHERE task_checklist_id = $1
RETURNING *;
-- name: DeleteTaskChecklistByID :exec
DELETE FROM task_checklist WHERE task_checklist_id = $1;
-- name: GetTaskChecklistByID :one
SELECT * FROM task_checklist WHERE task_checklist_id = $1;
-- name: CreateTaskChecklistItem :one
INSERT INTO task_checklist_item (task_checklist_id, created_at, name, position, complete, due_date) VALUES ($1, $2, $3, $4, false, null)
RETURNING *;
-- name: GetTaskChecklistItemsForTaskChecklist :many
SELECT * FROM task_checklist_item WHERE task_checklist_id = $1;
-- name: SetTaskChecklistItemComplete :one
UPDATE task_checklist_item SET complete = $2 WHERE task_checklist_item_id = $1
RETURNING *;
-- name: DeleteTaskChecklistItem :exec
DELETE FROM task_checklist_item WHERE task_checklist_item_id = $1;
-- name: GetTaskChecklistItemByID :one
SELECT * FROM task_checklist_item WHERE task_checklist_item_id = $1;
-- name: UpdateTaskChecklistItemName :one
UPDATE task_checklist_item SET name = $2 WHERE task_checklist_item_id = $1
RETURNING *;

View File

@ -0,0 +1,22 @@
-- name: CreateTaskGroup :one
INSERT INTO task_group (project_id, created_at, name, position)
VALUES($1, $2, $3, $4) RETURNING *;
-- name: GetTaskGroupsForProject :many
SELECT * FROM task_group WHERE project_id = $1;
-- name: GetAllTaskGroups :many
SELECT * FROM task_group;
-- name: GetTaskGroupByID :one
SELECT * FROM task_group WHERE task_group_id = $1;
-- name: SetTaskGroupName :one
UPDATE task_group SET name = $2 WHERE task_group_id = $1 RETURNING *;
-- name: DeleteTaskGroupByID :execrows
DELETE FROM task_group WHERE task_group_id = $1;
-- name: UpdateTaskGroupLocation :one
UPDATE task_group SET position = $2 WHERE task_group_id = $1 RETURNING *;

View File

@ -0,0 +1,18 @@
-- name: CreateTaskLabelForTask :one
INSERT INTO task_label (task_id, project_label_id, assigned_date)
VALUES ($1, $2, $3) RETURNING *;
-- name: GetTaskLabelsForTaskID :many
SELECT * FROM task_label WHERE task_id = $1;
-- name: GetTaskLabelByID :one
SELECT * FROM task_label WHERE task_label_id = $1;
-- name: DeleteTaskLabelByID :exec
DELETE FROM task_label WHERE task_label_id = $1;
-- name: GetTaskLabelForTaskByProjectLabelID :one
SELECT * FROM task_label WHERE task_id = $1 AND project_label_id = $2;
-- name: DeleteTaskLabelForTaskByProjectLabelID :exec
DELETE FROM task_label WHERE project_label_id = $2 AND task_id = $1;

View File

@ -0,0 +1,17 @@
-- name: GetAllTeams :many
SELECT * FROM team;
-- name: GetTeamByID :one
SELECT * FROM team WHERE team_id = $1;
-- name: CreateTeam :one
INSERT INTO team (organization_id, created_at, name, owner) VALUES ($1, $2, $3, $4) RETURNING *;
-- name: DeleteTeamByID :exec
DELETE FROM team WHERE team_id = $1;
-- name: GetTeamsForOrganization :many
SELECT * FROM team WHERE organization_id = $1;
-- name: SetTeamOwner :one
UPDATE team SET owner = $2 WHERE team_id = $1 RETURNING *;

View File

@ -0,0 +1,21 @@
-- name: CreateTeamMember :one
INSERT INTO team_member (team_id, user_id, addedDate, role_code) VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetTeamMembersForTeamID :many
SELECT * FROM team_member WHERE team_id = $1;
-- name: DeleteTeamMember :exec
DELETE FROM team_member WHERE user_id = $1 AND team_id = $2;
-- name: GetRoleForTeamMember :one
SELECT code, role.name FROM team_member
INNER JOIN role ON role.code = team_member.role_code
WHERE user_id = $1 AND team_id = $2;
-- name: UpdateTeamMemberRole :one
UPDATE team_member SET role_code = $3 WHERE user_id = $2 AND team_id = $1
RETURNING *;
-- name: GetTeamMemberByID :one
SELECT * FROM team_member WHERE team_id = $1 AND user_id = $2;

View File

@ -0,0 +1,14 @@
-- name: GetRefreshTokenByID :one
SELECT * FROM refresh_token WHERE token_id = $1;
-- name: CreateRefreshToken :one
INSERT INTO refresh_token (user_id, created_at, expires_at) VALUES ($1, $2, $3) RETURNING *;
-- name: DeleteRefreshTokenByID :exec
DELETE FROM refresh_token WHERE token_id = $1;
-- name: DeleteRefreshTokenByUserID :exec
DELETE FROM refresh_token WHERE user_id = $1;
-- name: DeleteExpiredTokens :exec
DELETE FROM refresh_token WHERE expires_at <= NOW();

View File

@ -0,0 +1,24 @@
-- name: GetUserAccountByID :one
SELECT * FROM user_account WHERE user_id = $1;
-- name: GetAllUserAccounts :many
SELECT * FROM user_account;
-- name: GetUserAccountByUsername :one
SELECT * FROM user_account WHERE username = $1;
-- name: CreateUserAccount :one
INSERT INTO user_account(full_name, initials, email, username, created_at, password_hash, role_code)
VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *;
-- name: UpdateUserAccountProfileAvatarURL :one
UPDATE user_account SET profile_avatar_url = $2 WHERE user_id = $1
RETURNING *;
-- name: DeleteUserAccountByID :exec
DELETE FROM user_account WHERE user_id = $1;
-- name: GetRoleForUserID :one
SELECT username, role.code, role.name FROM user_account
INNER JOIN role ON role.code = user_account.role_code
WHERE user_id = $1;

View File

@ -0,0 +1,18 @@
package db
import (
"github.com/jmoiron/sqlx"
)
type Repository struct {
*Queries
db *sqlx.DB
}
// NewRepository returns an implementation of the Repository interface.
func NewRepository(db *sqlx.DB) *Repository {
return &Repository{
Queries: New(db.DB),
db: db,
}
}

297
api/internal/db/task.sql.go Normal file
View File

@ -0,0 +1,297 @@
// Code generated by sqlc. DO NOT EDIT.
// source: task.sql
package db
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const createTask = `-- name: CreateTask :one
INSERT INTO task (task_group_id, created_at, name, position)
VALUES($1, $2, $3, $4) RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type CreateTaskParams struct {
TaskGroupID uuid.UUID `json:"task_group_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
func (q *Queries) CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error) {
row := q.db.QueryRowContext(ctx, createTask,
arg.TaskGroupID,
arg.CreatedAt,
arg.Name,
arg.Position,
)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const deleteTaskByID = `-- name: DeleteTaskByID :exec
DELETE FROM task WHERE task_id = $1
`
func (q *Queries) DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteTaskByID, taskID)
return err
}
const deleteTasksByTaskGroupID = `-- name: DeleteTasksByTaskGroupID :execrows
DELETE FROM task where task_group_id = $1
`
func (q *Queries) DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteTasksByTaskGroupID, taskGroupID)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const getAllTasks = `-- name: GetAllTasks :many
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete FROM task
`
func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
rows, err := q.db.QueryContext(ctx, getAllTasks)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Task
for rows.Next() {
var i Task
if err := rows.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProjectIDForTask = `-- name: GetProjectIDForTask :one
SELECT project_id FROM task
INNER JOIN task_group ON task_group.task_group_id = task.task_group_id
WHERE task_id = $1
`
func (q *Queries) GetProjectIDForTask(ctx context.Context, taskID uuid.UUID) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, getProjectIDForTask, taskID)
var project_id uuid.UUID
err := row.Scan(&project_id)
return project_id, err
}
const getTaskByID = `-- name: GetTaskByID :one
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete FROM task WHERE task_id = $1
`
func (q *Queries) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error) {
row := q.db.QueryRowContext(ctx, getTaskByID, taskID)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const getTasksForTaskGroupID = `-- name: GetTasksForTaskGroupID :many
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete FROM task WHERE task_group_id = $1
`
func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) {
rows, err := q.db.QueryContext(ctx, getTasksForTaskGroupID, taskGroupID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Task
for rows.Next() {
var i Task
if err := rows.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setTaskComplete = `-- name: SetTaskComplete :one
UPDATE task SET complete = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type SetTaskCompleteParams struct {
TaskID uuid.UUID `json:"task_id"`
Complete bool `json:"complete"`
}
func (q *Queries) SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error) {
row := q.db.QueryRowContext(ctx, setTaskComplete, arg.TaskID, arg.Complete)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const updateTaskDescription = `-- name: UpdateTaskDescription :one
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type UpdateTaskDescriptionParams struct {
TaskID uuid.UUID `json:"task_id"`
Description sql.NullString `json:"description"`
}
func (q *Queries) UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error) {
row := q.db.QueryRowContext(ctx, updateTaskDescription, arg.TaskID, arg.Description)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const updateTaskDueDate = `-- name: UpdateTaskDueDate :one
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type UpdateTaskDueDateParams struct {
TaskID uuid.UUID `json:"task_id"`
DueDate sql.NullTime `json:"due_date"`
}
func (q *Queries) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error) {
row := q.db.QueryRowContext(ctx, updateTaskDueDate, arg.TaskID, arg.DueDate)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const updateTaskLocation = `-- name: UpdateTaskLocation :one
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type UpdateTaskLocationParams struct {
TaskID uuid.UUID `json:"task_id"`
TaskGroupID uuid.UUID `json:"task_group_id"`
Position float64 `json:"position"`
}
func (q *Queries) UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error) {
row := q.db.QueryRowContext(ctx, updateTaskLocation, arg.TaskID, arg.TaskGroupID, arg.Position)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}
const updateTaskName = `-- name: UpdateTaskName :one
UPDATE task SET name = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete
`
type UpdateTaskNameParams struct {
TaskID uuid.UUID `json:"task_id"`
Name string `json:"name"`
}
func (q *Queries) UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error) {
row := q.db.QueryRowContext(ctx, updateTaskName, arg.TaskID, arg.Name)
var i Task
err := row.Scan(
&i.TaskID,
&i.TaskGroupID,
&i.CreatedAt,
&i.Name,
&i.Position,
&i.Description,
&i.DueDate,
&i.Complete,
)
return i, err
}

View File

@ -0,0 +1,87 @@
// Code generated by sqlc. DO NOT EDIT.
// source: task_assigned.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTaskAssigned = `-- name: CreateTaskAssigned :one
INSERT INTO task_assigned (task_id, user_id, assigned_date)
VALUES($1, $2, $3) RETURNING task_assigned_id, task_id, user_id, assigned_date
`
type CreateTaskAssignedParams struct {
TaskID uuid.UUID `json:"task_id"`
UserID uuid.UUID `json:"user_id"`
AssignedDate time.Time `json:"assigned_date"`
}
func (q *Queries) CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error) {
row := q.db.QueryRowContext(ctx, createTaskAssigned, arg.TaskID, arg.UserID, arg.AssignedDate)
var i TaskAssigned
err := row.Scan(
&i.TaskAssignedID,
&i.TaskID,
&i.UserID,
&i.AssignedDate,
)
return i, err
}
const deleteTaskAssignedByID = `-- name: DeleteTaskAssignedByID :one
DELETE FROM task_assigned WHERE task_id = $1 AND user_id = $2 RETURNING task_assigned_id, task_id, user_id, assigned_date
`
type DeleteTaskAssignedByIDParams struct {
TaskID uuid.UUID `json:"task_id"`
UserID uuid.UUID `json:"user_id"`
}
func (q *Queries) DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error) {
row := q.db.QueryRowContext(ctx, deleteTaskAssignedByID, arg.TaskID, arg.UserID)
var i TaskAssigned
err := row.Scan(
&i.TaskAssignedID,
&i.TaskID,
&i.UserID,
&i.AssignedDate,
)
return i, err
}
const getAssignedMembersForTask = `-- name: GetAssignedMembersForTask :many
SELECT task_assigned_id, task_id, user_id, assigned_date FROM task_assigned WHERE task_id = $1
`
func (q *Queries) GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error) {
rows, err := q.db.QueryContext(ctx, getAssignedMembersForTask, taskID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskAssigned
for rows.Next() {
var i TaskAssigned
if err := rows.Scan(
&i.TaskAssignedID,
&i.TaskID,
&i.UserID,
&i.AssignedDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -0,0 +1,268 @@
// Code generated by sqlc. DO NOT EDIT.
// source: task_checklist.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTaskChecklist = `-- name: CreateTaskChecklist :one
INSERT INTO task_checklist (task_id, created_at, name, position) VALUES ($1, $2, $3, $4)
RETURNING task_checklist_id, task_id, created_at, name, position
`
type CreateTaskChecklistParams struct {
TaskID uuid.UUID `json:"task_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
func (q *Queries) CreateTaskChecklist(ctx context.Context, arg CreateTaskChecklistParams) (TaskChecklist, error) {
row := q.db.QueryRowContext(ctx, createTaskChecklist,
arg.TaskID,
arg.CreatedAt,
arg.Name,
arg.Position,
)
var i TaskChecklist
err := row.Scan(
&i.TaskChecklistID,
&i.TaskID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}
const createTaskChecklistItem = `-- name: CreateTaskChecklistItem :one
INSERT INTO task_checklist_item (task_checklist_id, created_at, name, position, complete, due_date) VALUES ($1, $2, $3, $4, false, null)
RETURNING task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date
`
type CreateTaskChecklistItemParams struct {
TaskChecklistID uuid.UUID `json:"task_checklist_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
func (q *Queries) CreateTaskChecklistItem(ctx context.Context, arg CreateTaskChecklistItemParams) (TaskChecklistItem, error) {
row := q.db.QueryRowContext(ctx, createTaskChecklistItem,
arg.TaskChecklistID,
arg.CreatedAt,
arg.Name,
arg.Position,
)
var i TaskChecklistItem
err := row.Scan(
&i.TaskChecklistItemID,
&i.TaskChecklistID,
&i.CreatedAt,
&i.Complete,
&i.Name,
&i.Position,
&i.DueDate,
)
return i, err
}
const deleteTaskChecklistByID = `-- name: DeleteTaskChecklistByID :exec
DELETE FROM task_checklist WHERE task_checklist_id = $1
`
func (q *Queries) DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteTaskChecklistByID, taskChecklistID)
return err
}
const deleteTaskChecklistItem = `-- name: DeleteTaskChecklistItem :exec
DELETE FROM task_checklist_item WHERE task_checklist_item_id = $1
`
func (q *Queries) DeleteTaskChecklistItem(ctx context.Context, taskChecklistItemID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteTaskChecklistItem, taskChecklistItemID)
return err
}
const getTaskChecklistByID = `-- name: GetTaskChecklistByID :one
SELECT task_checklist_id, task_id, created_at, name, position FROM task_checklist WHERE task_checklist_id = $1
`
func (q *Queries) GetTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) (TaskChecklist, error) {
row := q.db.QueryRowContext(ctx, getTaskChecklistByID, taskChecklistID)
var i TaskChecklist
err := row.Scan(
&i.TaskChecklistID,
&i.TaskID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}
const getTaskChecklistItemByID = `-- name: GetTaskChecklistItemByID :one
SELECT task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date FROM task_checklist_item WHERE task_checklist_item_id = $1
`
func (q *Queries) GetTaskChecklistItemByID(ctx context.Context, taskChecklistItemID uuid.UUID) (TaskChecklistItem, error) {
row := q.db.QueryRowContext(ctx, getTaskChecklistItemByID, taskChecklistItemID)
var i TaskChecklistItem
err := row.Scan(
&i.TaskChecklistItemID,
&i.TaskChecklistID,
&i.CreatedAt,
&i.Complete,
&i.Name,
&i.Position,
&i.DueDate,
)
return i, err
}
const getTaskChecklistItemsForTaskChecklist = `-- name: GetTaskChecklistItemsForTaskChecklist :many
SELECT task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date FROM task_checklist_item WHERE task_checklist_id = $1
`
func (q *Queries) GetTaskChecklistItemsForTaskChecklist(ctx context.Context, taskChecklistID uuid.UUID) ([]TaskChecklistItem, error) {
rows, err := q.db.QueryContext(ctx, getTaskChecklistItemsForTaskChecklist, taskChecklistID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskChecklistItem
for rows.Next() {
var i TaskChecklistItem
if err := rows.Scan(
&i.TaskChecklistItemID,
&i.TaskChecklistID,
&i.CreatedAt,
&i.Complete,
&i.Name,
&i.Position,
&i.DueDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTaskChecklistsForTask = `-- name: GetTaskChecklistsForTask :many
SELECT task_checklist_id, task_id, created_at, name, position FROM task_checklist WHERE task_id = $1
`
func (q *Queries) GetTaskChecklistsForTask(ctx context.Context, taskID uuid.UUID) ([]TaskChecklist, error) {
rows, err := q.db.QueryContext(ctx, getTaskChecklistsForTask, taskID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskChecklist
for rows.Next() {
var i TaskChecklist
if err := rows.Scan(
&i.TaskChecklistID,
&i.TaskID,
&i.CreatedAt,
&i.Name,
&i.Position,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setTaskChecklistItemComplete = `-- name: SetTaskChecklistItemComplete :one
UPDATE task_checklist_item SET complete = $2 WHERE task_checklist_item_id = $1
RETURNING task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date
`
type SetTaskChecklistItemCompleteParams struct {
TaskChecklistItemID uuid.UUID `json:"task_checklist_item_id"`
Complete bool `json:"complete"`
}
func (q *Queries) SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error) {
row := q.db.QueryRowContext(ctx, setTaskChecklistItemComplete, arg.TaskChecklistItemID, arg.Complete)
var i TaskChecklistItem
err := row.Scan(
&i.TaskChecklistItemID,
&i.TaskChecklistID,
&i.CreatedAt,
&i.Complete,
&i.Name,
&i.Position,
&i.DueDate,
)
return i, err
}
const updateTaskChecklistItemName = `-- name: UpdateTaskChecklistItemName :one
UPDATE task_checklist_item SET name = $2 WHERE task_checklist_item_id = $1
RETURNING task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date
`
type UpdateTaskChecklistItemNameParams struct {
TaskChecklistItemID uuid.UUID `json:"task_checklist_item_id"`
Name string `json:"name"`
}
func (q *Queries) UpdateTaskChecklistItemName(ctx context.Context, arg UpdateTaskChecklistItemNameParams) (TaskChecklistItem, error) {
row := q.db.QueryRowContext(ctx, updateTaskChecklistItemName, arg.TaskChecklistItemID, arg.Name)
var i TaskChecklistItem
err := row.Scan(
&i.TaskChecklistItemID,
&i.TaskChecklistID,
&i.CreatedAt,
&i.Complete,
&i.Name,
&i.Position,
&i.DueDate,
)
return i, err
}
const updateTaskChecklistName = `-- name: UpdateTaskChecklistName :one
UPDATE task_checklist SET name = $2 WHERE task_checklist_id = $1
RETURNING task_checklist_id, task_id, created_at, name, position
`
type UpdateTaskChecklistNameParams struct {
TaskChecklistID uuid.UUID `json:"task_checklist_id"`
Name string `json:"name"`
}
func (q *Queries) UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error) {
row := q.db.QueryRowContext(ctx, updateTaskChecklistName, arg.TaskChecklistID, arg.Name)
var i TaskChecklist
err := row.Scan(
&i.TaskChecklistID,
&i.TaskID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}

View File

@ -0,0 +1,180 @@
// Code generated by sqlc. DO NOT EDIT.
// source: task_group.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTaskGroup = `-- name: CreateTaskGroup :one
INSERT INTO task_group (project_id, created_at, name, position)
VALUES($1, $2, $3, $4) RETURNING task_group_id, project_id, created_at, name, position
`
type CreateTaskGroupParams struct {
ProjectID uuid.UUID `json:"project_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Position float64 `json:"position"`
}
func (q *Queries) CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error) {
row := q.db.QueryRowContext(ctx, createTaskGroup,
arg.ProjectID,
arg.CreatedAt,
arg.Name,
arg.Position,
)
var i TaskGroup
err := row.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}
const deleteTaskGroupByID = `-- name: DeleteTaskGroupByID :execrows
DELETE FROM task_group WHERE task_group_id = $1
`
func (q *Queries) DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteTaskGroupByID, taskGroupID)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const getAllTaskGroups = `-- name: GetAllTaskGroups :many
SELECT task_group_id, project_id, created_at, name, position FROM task_group
`
func (q *Queries) GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error) {
rows, err := q.db.QueryContext(ctx, getAllTaskGroups)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskGroup
for rows.Next() {
var i TaskGroup
if err := rows.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTaskGroupByID = `-- name: GetTaskGroupByID :one
SELECT task_group_id, project_id, created_at, name, position FROM task_group WHERE task_group_id = $1
`
func (q *Queries) GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error) {
row := q.db.QueryRowContext(ctx, getTaskGroupByID, taskGroupID)
var i TaskGroup
err := row.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}
const getTaskGroupsForProject = `-- name: GetTaskGroupsForProject :many
SELECT task_group_id, project_id, created_at, name, position FROM task_group WHERE project_id = $1
`
func (q *Queries) GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error) {
rows, err := q.db.QueryContext(ctx, getTaskGroupsForProject, projectID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskGroup
for rows.Next() {
var i TaskGroup
if err := rows.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setTaskGroupName = `-- name: SetTaskGroupName :one
UPDATE task_group SET name = $2 WHERE task_group_id = $1 RETURNING task_group_id, project_id, created_at, name, position
`
type SetTaskGroupNameParams struct {
TaskGroupID uuid.UUID `json:"task_group_id"`
Name string `json:"name"`
}
func (q *Queries) SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error) {
row := q.db.QueryRowContext(ctx, setTaskGroupName, arg.TaskGroupID, arg.Name)
var i TaskGroup
err := row.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}
const updateTaskGroupLocation = `-- name: UpdateTaskGroupLocation :one
UPDATE task_group SET position = $2 WHERE task_group_id = $1 RETURNING task_group_id, project_id, created_at, name, position
`
type UpdateTaskGroupLocationParams struct {
TaskGroupID uuid.UUID `json:"task_group_id"`
Position float64 `json:"position"`
}
func (q *Queries) UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error) {
row := q.db.QueryRowContext(ctx, updateTaskGroupLocation, arg.TaskGroupID, arg.Position)
var i TaskGroup
err := row.Scan(
&i.TaskGroupID,
&i.ProjectID,
&i.CreatedAt,
&i.Name,
&i.Position,
)
return i, err
}

View File

@ -0,0 +1,126 @@
// Code generated by sqlc. DO NOT EDIT.
// source: task_label.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTaskLabelForTask = `-- name: CreateTaskLabelForTask :one
INSERT INTO task_label (task_id, project_label_id, assigned_date)
VALUES ($1, $2, $3) RETURNING task_label_id, task_id, project_label_id, assigned_date
`
type CreateTaskLabelForTaskParams struct {
TaskID uuid.UUID `json:"task_id"`
ProjectLabelID uuid.UUID `json:"project_label_id"`
AssignedDate time.Time `json:"assigned_date"`
}
func (q *Queries) CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error) {
row := q.db.QueryRowContext(ctx, createTaskLabelForTask, arg.TaskID, arg.ProjectLabelID, arg.AssignedDate)
var i TaskLabel
err := row.Scan(
&i.TaskLabelID,
&i.TaskID,
&i.ProjectLabelID,
&i.AssignedDate,
)
return i, err
}
const deleteTaskLabelByID = `-- name: DeleteTaskLabelByID :exec
DELETE FROM task_label WHERE task_label_id = $1
`
func (q *Queries) DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteTaskLabelByID, taskLabelID)
return err
}
const deleteTaskLabelForTaskByProjectLabelID = `-- name: DeleteTaskLabelForTaskByProjectLabelID :exec
DELETE FROM task_label WHERE project_label_id = $2 AND task_id = $1
`
type DeleteTaskLabelForTaskByProjectLabelIDParams struct {
TaskID uuid.UUID `json:"task_id"`
ProjectLabelID uuid.UUID `json:"project_label_id"`
}
func (q *Queries) DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error {
_, err := q.db.ExecContext(ctx, deleteTaskLabelForTaskByProjectLabelID, arg.TaskID, arg.ProjectLabelID)
return err
}
const getTaskLabelByID = `-- name: GetTaskLabelByID :one
SELECT task_label_id, task_id, project_label_id, assigned_date FROM task_label WHERE task_label_id = $1
`
func (q *Queries) GetTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) (TaskLabel, error) {
row := q.db.QueryRowContext(ctx, getTaskLabelByID, taskLabelID)
var i TaskLabel
err := row.Scan(
&i.TaskLabelID,
&i.TaskID,
&i.ProjectLabelID,
&i.AssignedDate,
)
return i, err
}
const getTaskLabelForTaskByProjectLabelID = `-- name: GetTaskLabelForTaskByProjectLabelID :one
SELECT task_label_id, task_id, project_label_id, assigned_date FROM task_label WHERE task_id = $1 AND project_label_id = $2
`
type GetTaskLabelForTaskByProjectLabelIDParams struct {
TaskID uuid.UUID `json:"task_id"`
ProjectLabelID uuid.UUID `json:"project_label_id"`
}
func (q *Queries) GetTaskLabelForTaskByProjectLabelID(ctx context.Context, arg GetTaskLabelForTaskByProjectLabelIDParams) (TaskLabel, error) {
row := q.db.QueryRowContext(ctx, getTaskLabelForTaskByProjectLabelID, arg.TaskID, arg.ProjectLabelID)
var i TaskLabel
err := row.Scan(
&i.TaskLabelID,
&i.TaskID,
&i.ProjectLabelID,
&i.AssignedDate,
)
return i, err
}
const getTaskLabelsForTaskID = `-- name: GetTaskLabelsForTaskID :many
SELECT task_label_id, task_id, project_label_id, assigned_date FROM task_label WHERE task_id = $1
`
func (q *Queries) GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error) {
rows, err := q.db.QueryContext(ctx, getTaskLabelsForTaskID, taskID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TaskLabel
for rows.Next() {
var i TaskLabel
if err := rows.Scan(
&i.TaskLabelID,
&i.TaskID,
&i.ProjectLabelID,
&i.AssignedDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

154
api/internal/db/team.sql.go Normal file
View File

@ -0,0 +1,154 @@
// Code generated by sqlc. DO NOT EDIT.
// source: team.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTeam = `-- name: CreateTeam :one
INSERT INTO team (organization_id, created_at, name, owner) VALUES ($1, $2, $3, $4) RETURNING team_id, created_at, name, organization_id, owner
`
type CreateTeamParams struct {
OrganizationID uuid.UUID `json:"organization_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Owner uuid.UUID `json:"owner"`
}
func (q *Queries) CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error) {
row := q.db.QueryRowContext(ctx, createTeam,
arg.OrganizationID,
arg.CreatedAt,
arg.Name,
arg.Owner,
)
var i Team
err := row.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
)
return i, err
}
const deleteTeamByID = `-- name: DeleteTeamByID :exec
DELETE FROM team WHERE team_id = $1
`
func (q *Queries) DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteTeamByID, teamID)
return err
}
const getAllTeams = `-- name: GetAllTeams :many
SELECT team_id, created_at, name, organization_id, owner FROM team
`
func (q *Queries) GetAllTeams(ctx context.Context) ([]Team, error) {
rows, err := q.db.QueryContext(ctx, getAllTeams)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Team
for rows.Next() {
var i Team
if err := rows.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTeamByID = `-- name: GetTeamByID :one
SELECT team_id, created_at, name, organization_id, owner FROM team WHERE team_id = $1
`
func (q *Queries) GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error) {
row := q.db.QueryRowContext(ctx, getTeamByID, teamID)
var i Team
err := row.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
)
return i, err
}
const getTeamsForOrganization = `-- name: GetTeamsForOrganization :many
SELECT team_id, created_at, name, organization_id, owner FROM team WHERE organization_id = $1
`
func (q *Queries) GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error) {
rows, err := q.db.QueryContext(ctx, getTeamsForOrganization, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Team
for rows.Next() {
var i Team
if err := rows.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setTeamOwner = `-- name: SetTeamOwner :one
UPDATE team SET owner = $2 WHERE team_id = $1 RETURNING team_id, created_at, name, organization_id, owner
`
type SetTeamOwnerParams struct {
TeamID uuid.UUID `json:"team_id"`
Owner uuid.UUID `json:"owner"`
}
func (q *Queries) SetTeamOwner(ctx context.Context, arg SetTeamOwnerParams) (Team, error) {
row := q.db.QueryRowContext(ctx, setTeamOwner, arg.TeamID, arg.Owner)
var i Team
err := row.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
)
return i, err
}

View File

@ -0,0 +1,152 @@
// Code generated by sqlc. DO NOT EDIT.
// source: team_member.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createTeamMember = `-- name: CreateTeamMember :one
INSERT INTO team_member (team_id, user_id, addedDate, role_code) VALUES ($1, $2, $3, $4)
RETURNING team_member_id, team_id, user_id, addeddate, role_code
`
type CreateTeamMemberParams struct {
TeamID uuid.UUID `json:"team_id"`
UserID uuid.UUID `json:"user_id"`
Addeddate time.Time `json:"addeddate"`
RoleCode string `json:"role_code"`
}
func (q *Queries) CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error) {
row := q.db.QueryRowContext(ctx, createTeamMember,
arg.TeamID,
arg.UserID,
arg.Addeddate,
arg.RoleCode,
)
var i TeamMember
err := row.Scan(
&i.TeamMemberID,
&i.TeamID,
&i.UserID,
&i.Addeddate,
&i.RoleCode,
)
return i, err
}
const deleteTeamMember = `-- name: DeleteTeamMember :exec
DELETE FROM team_member WHERE user_id = $1 AND team_id = $2
`
type DeleteTeamMemberParams struct {
UserID uuid.UUID `json:"user_id"`
TeamID uuid.UUID `json:"team_id"`
}
func (q *Queries) DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error {
_, err := q.db.ExecContext(ctx, deleteTeamMember, arg.UserID, arg.TeamID)
return err
}
const getRoleForTeamMember = `-- name: GetRoleForTeamMember :one
SELECT code, role.name FROM team_member
INNER JOIN role ON role.code = team_member.role_code
WHERE user_id = $1 AND team_id = $2
`
type GetRoleForTeamMemberParams struct {
UserID uuid.UUID `json:"user_id"`
TeamID uuid.UUID `json:"team_id"`
}
func (q *Queries) GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error) {
row := q.db.QueryRowContext(ctx, getRoleForTeamMember, arg.UserID, arg.TeamID)
var i Role
err := row.Scan(&i.Code, &i.Name)
return i, err
}
const getTeamMemberByID = `-- name: GetTeamMemberByID :one
SELECT team_member_id, team_id, user_id, addeddate, role_code FROM team_member WHERE team_id = $1 AND user_id = $2
`
type GetTeamMemberByIDParams struct {
TeamID uuid.UUID `json:"team_id"`
UserID uuid.UUID `json:"user_id"`
}
func (q *Queries) GetTeamMemberByID(ctx context.Context, arg GetTeamMemberByIDParams) (TeamMember, error) {
row := q.db.QueryRowContext(ctx, getTeamMemberByID, arg.TeamID, arg.UserID)
var i TeamMember
err := row.Scan(
&i.TeamMemberID,
&i.TeamID,
&i.UserID,
&i.Addeddate,
&i.RoleCode,
)
return i, err
}
const getTeamMembersForTeamID = `-- name: GetTeamMembersForTeamID :many
SELECT team_member_id, team_id, user_id, addeddate, role_code FROM team_member WHERE team_id = $1
`
func (q *Queries) GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error) {
rows, err := q.db.QueryContext(ctx, getTeamMembersForTeamID, teamID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TeamMember
for rows.Next() {
var i TeamMember
if err := rows.Scan(
&i.TeamMemberID,
&i.TeamID,
&i.UserID,
&i.Addeddate,
&i.RoleCode,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateTeamMemberRole = `-- name: UpdateTeamMemberRole :one
UPDATE team_member SET role_code = $3 WHERE user_id = $2 AND team_id = $1
RETURNING team_member_id, team_id, user_id, addeddate, role_code
`
type UpdateTeamMemberRoleParams struct {
TeamID uuid.UUID `json:"team_id"`
UserID uuid.UUID `json:"user_id"`
RoleCode string `json:"role_code"`
}
func (q *Queries) UpdateTeamMemberRole(ctx context.Context, arg UpdateTeamMemberRoleParams) (TeamMember, error) {
row := q.db.QueryRowContext(ctx, updateTeamMemberRole, arg.TeamID, arg.UserID, arg.RoleCode)
var i TeamMember
err := row.Scan(
&i.TeamMemberID,
&i.TeamID,
&i.UserID,
&i.Addeddate,
&i.RoleCode,
)
return i, err
}

View File

@ -0,0 +1,76 @@
// Code generated by sqlc. DO NOT EDIT.
// source: token.sql
package db
import (
"context"
"time"
"github.com/google/uuid"
)
const createRefreshToken = `-- name: CreateRefreshToken :one
INSERT INTO refresh_token (user_id, created_at, expires_at) VALUES ($1, $2, $3) RETURNING token_id, user_id, created_at, expires_at
`
type CreateRefreshTokenParams struct {
UserID uuid.UUID `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
func (q *Queries) CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error) {
row := q.db.QueryRowContext(ctx, createRefreshToken, arg.UserID, arg.CreatedAt, arg.ExpiresAt)
var i RefreshToken
err := row.Scan(
&i.TokenID,
&i.UserID,
&i.CreatedAt,
&i.ExpiresAt,
)
return i, err
}
const deleteExpiredTokens = `-- name: DeleteExpiredTokens :exec
DELETE FROM refresh_token WHERE expires_at <= NOW()
`
func (q *Queries) DeleteExpiredTokens(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, deleteExpiredTokens)
return err
}
const deleteRefreshTokenByID = `-- name: DeleteRefreshTokenByID :exec
DELETE FROM refresh_token WHERE token_id = $1
`
func (q *Queries) DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteRefreshTokenByID, tokenID)
return err
}
const deleteRefreshTokenByUserID = `-- name: DeleteRefreshTokenByUserID :exec
DELETE FROM refresh_token WHERE user_id = $1
`
func (q *Queries) DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteRefreshTokenByUserID, userID)
return err
}
const getRefreshTokenByID = `-- name: GetRefreshTokenByID :one
SELECT token_id, user_id, created_at, expires_at FROM refresh_token WHERE token_id = $1
`
func (q *Queries) GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error) {
row := q.db.QueryRowContext(ctx, getRefreshTokenByID, tokenID)
var i RefreshToken
err := row.Scan(
&i.TokenID,
&i.UserID,
&i.CreatedAt,
&i.ExpiresAt,
)
return i, err
}

View File

@ -0,0 +1,191 @@
// Code generated by sqlc. DO NOT EDIT.
// source: user_accounts.sql
package db
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const createUserAccount = `-- name: CreateUserAccount :one
INSERT INTO user_account(full_name, initials, email, username, created_at, password_hash, role_code)
VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code
`
type CreateUserAccountParams struct {
FullName string `json:"full_name"`
Initials string `json:"initials"`
Email string `json:"email"`
Username string `json:"username"`
CreatedAt time.Time `json:"created_at"`
PasswordHash string `json:"password_hash"`
RoleCode string `json:"role_code"`
}
func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error) {
row := q.db.QueryRowContext(ctx, createUserAccount,
arg.FullName,
arg.Initials,
arg.Email,
arg.Username,
arg.CreatedAt,
arg.PasswordHash,
arg.RoleCode,
)
var i UserAccount
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.Email,
&i.Username,
&i.PasswordHash,
&i.ProfileBgColor,
&i.FullName,
&i.Initials,
&i.ProfileAvatarUrl,
&i.RoleCode,
)
return i, err
}
const deleteUserAccountByID = `-- name: DeleteUserAccountByID :exec
DELETE FROM user_account WHERE user_id = $1
`
func (q *Queries) DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteUserAccountByID, userID)
return err
}
const getAllUserAccounts = `-- name: GetAllUserAccounts :many
SELECT user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code FROM user_account
`
func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) {
rows, err := q.db.QueryContext(ctx, getAllUserAccounts)
if err != nil {
return nil, err
}
defer rows.Close()
var items []UserAccount
for rows.Next() {
var i UserAccount
if err := rows.Scan(
&i.UserID,
&i.CreatedAt,
&i.Email,
&i.Username,
&i.PasswordHash,
&i.ProfileBgColor,
&i.FullName,
&i.Initials,
&i.ProfileAvatarUrl,
&i.RoleCode,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getRoleForUserID = `-- name: GetRoleForUserID :one
SELECT username, role.code, role.name FROM user_account
INNER JOIN role ON role.code = user_account.role_code
WHERE user_id = $1
`
type GetRoleForUserIDRow struct {
Username string `json:"username"`
Code string `json:"code"`
Name string `json:"name"`
}
func (q *Queries) GetRoleForUserID(ctx context.Context, userID uuid.UUID) (GetRoleForUserIDRow, error) {
row := q.db.QueryRowContext(ctx, getRoleForUserID, userID)
var i GetRoleForUserIDRow
err := row.Scan(&i.Username, &i.Code, &i.Name)
return i, err
}
const getUserAccountByID = `-- name: GetUserAccountByID :one
SELECT user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code FROM user_account WHERE user_id = $1
`
func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) {
row := q.db.QueryRowContext(ctx, getUserAccountByID, userID)
var i UserAccount
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.Email,
&i.Username,
&i.PasswordHash,
&i.ProfileBgColor,
&i.FullName,
&i.Initials,
&i.ProfileAvatarUrl,
&i.RoleCode,
)
return i, err
}
const getUserAccountByUsername = `-- name: GetUserAccountByUsername :one
SELECT user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code FROM user_account WHERE username = $1
`
func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error) {
row := q.db.QueryRowContext(ctx, getUserAccountByUsername, username)
var i UserAccount
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.Email,
&i.Username,
&i.PasswordHash,
&i.ProfileBgColor,
&i.FullName,
&i.Initials,
&i.ProfileAvatarUrl,
&i.RoleCode,
)
return i, err
}
const updateUserAccountProfileAvatarURL = `-- name: UpdateUserAccountProfileAvatarURL :one
UPDATE user_account SET profile_avatar_url = $2 WHERE user_id = $1
RETURNING user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code
`
type UpdateUserAccountProfileAvatarURLParams struct {
UserID uuid.UUID `json:"user_id"`
ProfileAvatarUrl sql.NullString `json:"profile_avatar_url"`
}
func (q *Queries) UpdateUserAccountProfileAvatarURL(ctx context.Context, arg UpdateUserAccountProfileAvatarURLParams) (UserAccount, error) {
row := q.db.QueryRowContext(ctx, updateUserAccountProfileAvatarURL, arg.UserID, arg.ProfileAvatarUrl)
var i UserAccount
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.Email,
&i.Username,
&i.PasswordHash,
&i.ProfileBgColor,
&i.FullName,
&i.Initials,
&i.ProfileAvatarUrl,
&i.RoleCode,
)
return i, err
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
package graph
import (
"context"
"net/http"
"os"
"time"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/google/uuid"
"github.com/jordanknott/project-citadel/api/internal/db"
)
// NewHandler returns a new graphql endpoint handler.
func NewHandler(repo db.Repository) http.Handler {
srv := handler.New(NewExecutableSchema(Config{
Resolvers: &Resolver{
Repository: repo,
},
}))
srv.AddTransport(transport.Websocket{
KeepAlivePingInterval: 10 * time.Second,
})
srv.AddTransport(transport.Options{})
srv.AddTransport(transport.GET{})
srv.AddTransport(transport.POST{})
srv.AddTransport(transport.MultipartForm{})
srv.SetQueryCache(lru.New(1000))
srv.Use(extension.AutomaticPersistedQuery{
Cache: lru.New(100),
})
if isProd := os.Getenv("PRODUCTION") == "true"; isProd {
srv.Use(extension.FixedComplexityLimit(10))
} else {
srv.Use(extension.Introspection{})
}
return srv
}
// NewPlaygroundHandler returns a new GraphQL Playground handler.
func NewPlaygroundHandler(endpoint string) http.Handler {
return playground.Handler("GraphQL Playground", endpoint)
}
func GetUserID(ctx context.Context) (uuid.UUID, bool) {
userID, ok := ctx.Value("userID").(uuid.UUID)
return userID, ok
}

View File

@ -0,0 +1,418 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package graph
import (
"fmt"
"io"
"strconv"
"time"
"github.com/google/uuid"
"github.com/jordanknott/project-citadel/api/internal/db"
)
type AddTaskLabelInput struct {
TaskID uuid.UUID `json:"taskID"`
ProjectLabelID uuid.UUID `json:"projectLabelID"`
}
type AssignTaskInput struct {
TaskID uuid.UUID `json:"taskID"`
UserID uuid.UUID `json:"userID"`
}
type ChecklistBadge struct {
Complete int `json:"complete"`
Total int `json:"total"`
}
type CreateProjectMember struct {
ProjectID uuid.UUID `json:"projectID"`
UserID uuid.UUID `json:"userID"`
}
type CreateProjectMemberPayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
}
type CreateTaskChecklist struct {
TaskID uuid.UUID `json:"taskID"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type CreateTaskChecklistItem struct {
TaskChecklistID uuid.UUID `json:"taskChecklistID"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type CreateTeamMember struct {
UserID uuid.UUID `json:"userID"`
TeamID uuid.UUID `json:"teamID"`
}
type CreateTeamMemberPayload struct {
Team *db.Team `json:"team"`
TeamMember *Member `json:"teamMember"`
}
type DeleteProject struct {
ProjectID uuid.UUID `json:"projectID"`
}
type DeleteProjectLabel struct {
ProjectLabelID uuid.UUID `json:"projectLabelID"`
}
type DeleteProjectMember struct {
ProjectID uuid.UUID `json:"projectID"`
UserID uuid.UUID `json:"userID"`
}
type DeleteProjectMemberPayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
ProjectID uuid.UUID `json:"projectID"`
}
type DeleteProjectPayload struct {
Ok bool `json:"ok"`
Project *db.Project `json:"project"`
}
type DeleteTaskChecklist struct {
TaskChecklistID uuid.UUID `json:"taskChecklistID"`
}
type DeleteTaskChecklistItem struct {
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
}
type DeleteTaskChecklistItemPayload struct {
Ok bool `json:"ok"`
TaskChecklistItem *db.TaskChecklistItem `json:"taskChecklistItem"`
}
type DeleteTaskChecklistPayload struct {
Ok bool `json:"ok"`
TaskChecklist *db.TaskChecklist `json:"taskChecklist"`
}
type DeleteTaskGroupInput struct {
TaskGroupID uuid.UUID `json:"taskGroupID"`
}
type DeleteTaskGroupPayload struct {
Ok bool `json:"ok"`
AffectedRows int `json:"affectedRows"`
TaskGroup *db.TaskGroup `json:"taskGroup"`
}
type DeleteTaskInput struct {
TaskID string `json:"taskID"`
}
type DeleteTaskPayload struct {
TaskID string `json:"taskID"`
}
type DeleteTeam struct {
TeamID uuid.UUID `json:"teamID"`
}
type DeleteTeamMember struct {
TeamID uuid.UUID `json:"teamID"`
UserID uuid.UUID `json:"userID"`
}
type DeleteTeamMemberPayload struct {
TeamID uuid.UUID `json:"teamID"`
UserID uuid.UUID `json:"userID"`
}
type DeleteTeamPayload struct {
Ok bool `json:"ok"`
Team *db.Team `json:"team"`
Projects []db.Project `json:"projects"`
}
type DeleteUserAccount struct {
UserID uuid.UUID `json:"userID"`
}
type DeleteUserAccountPayload struct {
Ok bool `json:"ok"`
UserAccount *db.UserAccount `json:"userAccount"`
}
type FindProject struct {
ProjectID string `json:"projectId"`
}
type FindTask struct {
TaskID uuid.UUID `json:"taskID"`
}
type FindTeam struct {
TeamID uuid.UUID `json:"teamID"`
}
type FindUser struct {
UserID string `json:"userId"`
}
type LogoutUser struct {
UserID string `json:"userID"`
}
type Member struct {
ID uuid.UUID `json:"id"`
Role *db.Role `json:"role"`
FullName string `json:"fullName"`
Username string `json:"username"`
ProfileIcon *ProfileIcon `json:"profileIcon"`
}
type NewProject struct {
UserID uuid.UUID `json:"userID"`
TeamID uuid.UUID `json:"teamID"`
Name string `json:"name"`
}
type NewProjectLabel struct {
ProjectID uuid.UUID `json:"projectID"`
LabelColorID uuid.UUID `json:"labelColorID"`
Name *string `json:"name"`
}
type NewRefreshToken struct {
UserID string `json:"userId"`
}
type NewTask struct {
TaskGroupID string `json:"taskGroupID"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type NewTaskGroup struct {
ProjectID string `json:"projectID"`
Name string `json:"name"`
Position float64 `json:"position"`
}
type NewTaskGroupLocation struct {
TaskGroupID uuid.UUID `json:"taskGroupID"`
Position float64 `json:"position"`
}
type NewTaskLocation struct {
TaskID uuid.UUID `json:"taskID"`
TaskGroupID uuid.UUID `json:"taskGroupID"`
Position float64 `json:"position"`
}
type NewTeam struct {
Name string `json:"name"`
OrganizationID uuid.UUID `json:"organizationID"`
}
type NewUserAccount struct {
Username string `json:"username"`
Email string `json:"email"`
FullName string `json:"fullName"`
Initials string `json:"initials"`
Password string `json:"password"`
RoleCode string `json:"roleCode"`
}
type ProfileIcon struct {
URL *string `json:"url"`
Initials *string `json:"initials"`
BgColor *string `json:"bgColor"`
}
type ProjectsFilter struct {
TeamID *uuid.UUID `json:"teamID"`
}
type RemoveTaskLabelInput struct {
TaskLabelID uuid.UUID `json:"taskLabelID"`
}
type SetProjectOwner struct {
ProjectID uuid.UUID `json:"projectID"`
OwnerID uuid.UUID `json:"ownerID"`
}
type SetProjectOwnerPayload struct {
Ok bool `json:"ok"`
PrevOwner *Member `json:"prevOwner"`
NewOwner *Member `json:"newOwner"`
}
type SetTaskChecklistItemComplete struct {
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
Complete bool `json:"complete"`
}
type SetTaskComplete struct {
TaskID uuid.UUID `json:"taskID"`
Complete bool `json:"complete"`
}
type SetTeamOwner struct {
TeamID uuid.UUID `json:"teamID"`
UserID uuid.UUID `json:"userID"`
}
type SetTeamOwnerPayload struct {
Ok bool `json:"ok"`
PrevOwner *Member `json:"prevOwner"`
NewOwner *Member `json:"newOwner"`
}
type TaskBadges struct {
Checklist *ChecklistBadge `json:"checklist"`
}
type ToggleTaskLabelInput struct {
TaskID uuid.UUID `json:"taskID"`
ProjectLabelID uuid.UUID `json:"projectLabelID"`
}
type ToggleTaskLabelPayload struct {
Active bool `json:"active"`
Task *db.Task `json:"task"`
}
type UnassignTaskInput struct {
TaskID uuid.UUID `json:"taskID"`
UserID uuid.UUID `json:"userID"`
}
type UpdateProjectLabel struct {
ProjectLabelID uuid.UUID `json:"projectLabelID"`
LabelColorID uuid.UUID `json:"labelColorID"`
Name string `json:"name"`
}
type UpdateProjectLabelColor struct {
ProjectLabelID uuid.UUID `json:"projectLabelID"`
LabelColorID uuid.UUID `json:"labelColorID"`
}
type UpdateProjectLabelName struct {
ProjectLabelID uuid.UUID `json:"projectLabelID"`
Name string `json:"name"`
}
type UpdateProjectMemberRole struct {
ProjectID uuid.UUID `json:"projectID"`
UserID uuid.UUID `json:"userID"`
RoleCode RoleCode `json:"roleCode"`
}
type UpdateProjectMemberRolePayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
}
type UpdateProjectName struct {
ProjectID uuid.UUID `json:"projectID"`
Name string `json:"name"`
}
type UpdateTaskChecklistItemName struct {
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
Name string `json:"name"`
}
type UpdateTaskChecklistName struct {
TaskChecklistID uuid.UUID `json:"taskChecklistID"`
Name string `json:"name"`
}
type UpdateTaskDescriptionInput struct {
TaskID uuid.UUID `json:"taskID"`
Description string `json:"description"`
}
type UpdateTaskDueDate struct {
TaskID uuid.UUID `json:"taskID"`
DueDate *time.Time `json:"dueDate"`
}
type UpdateTaskGroupName struct {
TaskGroupID uuid.UUID `json:"taskGroupID"`
Name string `json:"name"`
}
type UpdateTaskLocationPayload struct {
PreviousTaskGroupID uuid.UUID `json:"previousTaskGroupID"`
Task *db.Task `json:"task"`
}
type UpdateTaskName struct {
TaskID string `json:"taskID"`
Name string `json:"name"`
}
type UpdateTeamMemberRole struct {
TeamID uuid.UUID `json:"teamID"`
UserID uuid.UUID `json:"userID"`
RoleCode RoleCode `json:"roleCode"`
}
type UpdateTeamMemberRolePayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
}
type RoleCode string
const (
RoleCodeOwner RoleCode = "owner"
RoleCodeAdmin RoleCode = "admin"
RoleCodeMember RoleCode = "member"
RoleCodeObserver RoleCode = "observer"
)
var AllRoleCode = []RoleCode{
RoleCodeOwner,
RoleCodeAdmin,
RoleCodeMember,
RoleCodeObserver,
}
func (e RoleCode) IsValid() bool {
switch e {
case RoleCodeOwner, RoleCodeAdmin, RoleCodeMember, RoleCodeObserver:
return true
}
return false
}
func (e RoleCode) String() string {
return string(e)
}
func (e *RoleCode) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = RoleCode(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid RoleCode", str)
}
return nil
}
func (e RoleCode) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}

View File

@ -0,0 +1,14 @@
//go:generate sh ../scripts/genSchema.sh
//go:generate go run github.com/99designs/gqlgen
package graph
import (
"sync"
"github.com/jordanknott/project-citadel/api/internal/db"
)
type Resolver struct {
Repository db.Repository
mu sync.Mutex
}

View File

@ -0,0 +1,23 @@
package graph
import (
"io"
"github.com/99designs/gqlgen/graphql"
"github.com/google/uuid"
"github.com/pkg/errors"
"strconv"
)
func MarshalUUID(uuid uuid.UUID) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte(strconv.Quote(uuid.String())))
})
}
func UnmarshalUUID(v interface{}) (uuid.UUID, error) {
if uuidRaw, ok := v.(string); ok {
return uuid.Parse(uuidRaw)
}
return uuid.UUID{}, errors.New("uuid must be a string")
}

View File

@ -0,0 +1,567 @@
scalar Time
scalar UUID
scalar Upload
enum RoleCode {
owner
admin
member
observer
}
type ProjectLabel {
id: ID!
createdDate: Time!
labelColor: LabelColor!
name: String
}
type LabelColor {
id: ID!
name: String!
position: Float!
colorHex: String!
}
type TaskLabel {
id: ID!
projectLabel: ProjectLabel!
assignedDate: Time!
}
type ProfileIcon {
url: String
initials: String
bgColor: String
}
type Member {
id: ID!
role: Role!
fullName: String!
username: String!
profileIcon: ProfileIcon!
}
type RefreshToken {
id: ID!
userId: UUID!
expiresAt: Time!
createdAt: Time!
}
type Role {
code: String!
name: String!
}
type UserAccount {
id: ID!
email: String!
createdAt: Time!
fullName: String!
initials: String!
role: Role!
username: String!
profileIcon: ProfileIcon!
}
type Team {
id: ID!
createdAt: Time!
name: String!
members: [Member!]!
}
type Project {
id: ID!
createdAt: Time!
name: String!
team: Team!
owner: Member!
taskGroups: [TaskGroup!]!
members: [Member!]!
labels: [ProjectLabel!]!
}
type TaskGroup {
id: ID!
projectID: String!
createdAt: Time!
name: String!
position: Float!
tasks: [Task!]!
}
type ChecklistBadge {
complete: Int!
total: Int!
}
type TaskBadges {
checklist: ChecklistBadge
}
type Task {
id: ID!
taskGroup: TaskGroup!
createdAt: Time!
name: String!
position: Float!
description: String
dueDate: Time
complete: Boolean!
assigned: [Member!]!
labels: [TaskLabel!]!
checklists: [TaskChecklist!]!
badges: TaskBadges!
}
type Organization {
id: ID!
name: String!
}
type TaskChecklistItem {
id: ID!
name: String!
taskChecklistID: UUID!
complete: Boolean!
position: Float!
dueDate: Time!
}
type TaskChecklist {
id: ID!
name: String!
position: Float!
items: [TaskChecklistItem!]!
}
type Query {
organizations: [Organization!]!
users: [UserAccount!]!
findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): Project!
findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team!
teams: [Team!]!
labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]!
me: UserAccount!
}
type Mutation
input ProjectsFilter {
teamID: UUID
}
input FindUser {
userId: String!
}
input FindProject {
projectId: String!
}
input FindTask {
taskID: UUID!
}
input FindTeam {
teamID: UUID!
}
extend type Mutation {
createProject(input: NewProject!): Project!
deleteProject(input: DeleteProject!): DeleteProjectPayload!
updateProjectName(input: UpdateProjectName): Project!
}
input NewProject {
userID: UUID!
teamID: UUID!
name: String!
}
input UpdateProjectName {
projectID: UUID!
name: String!
}
input DeleteProject {
projectID: UUID!
}
type DeleteProjectPayload {
ok: Boolean!
project: Project!
}
extend type Mutation {
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
deleteProjectLabel(input: DeleteProjectLabel!): ProjectLabel!
updateProjectLabel(input: UpdateProjectLabel!): ProjectLabel!
updateProjectLabelName(input: UpdateProjectLabelName!): ProjectLabel!
updateProjectLabelColor(input: UpdateProjectLabelColor!): ProjectLabel!
}
input NewProjectLabel {
projectID: UUID!
labelColorID: UUID!
name: String
}
input DeleteProjectLabel {
projectLabelID: UUID!
}
input UpdateProjectLabelName {
projectLabelID: UUID!
name: String!
}
input UpdateProjectLabel {
projectLabelID: UUID!
labelColorID: UUID!
name: String!
}
input UpdateProjectLabelColor {
projectLabelID: UUID!
labelColorID: UUID!
}
extend type Mutation {
createProjectMember(input: CreateProjectMember!): CreateProjectMemberPayload!
deleteProjectMember(input: DeleteProjectMember!): DeleteProjectMemberPayload!
updateProjectMemberRole(input: UpdateProjectMemberRole!): UpdateProjectMemberRolePayload!
setProjectOwner(input: SetProjectOwner!): SetProjectOwnerPayload!
}
input CreateProjectMember {
projectID: UUID!
userID: UUID!
}
type CreateProjectMemberPayload {
ok: Boolean!
member: Member!
}
input DeleteProjectMember {
projectID: UUID!
userID: UUID!
}
type DeleteProjectMemberPayload {
ok: Boolean!
member: Member!
projectID: UUID!
}
input UpdateProjectMemberRole {
projectID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateProjectMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetProjectOwner {
projectID: UUID!
ownerID: UUID!
}
type SetProjectOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}
extend type Mutation {
createTask(input: NewTask!): Task!
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
updateTaskName(input: UpdateTaskName!): Task!
setTaskComplete(input: SetTaskComplete!): Task!
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
assignTask(input: AssignTaskInput): Task!
unassignTask(input: UnassignTaskInput): Task!
}
input NewTask {
taskGroupID: String!
name: String!
position: Float!
}
input AssignTaskInput {
taskID: UUID!
userID: UUID!
}
input UnassignTaskInput {
taskID: UUID!
userID: UUID!
}
input UpdateTaskDescriptionInput {
taskID: UUID!
description: String!
}
type UpdateTaskLocationPayload {
previousTaskGroupID: UUID!
task: Task!
}
input UpdateTaskDueDate {
taskID: UUID!
dueDate: Time
}
input SetTaskComplete {
taskID: UUID!
complete: Boolean!
}
input NewTaskLocation {
taskID: UUID!
taskGroupID: UUID!
position: Float!
}
input DeleteTaskInput {
taskID: String!
}
type DeleteTaskPayload {
taskID: String!
}
input UpdateTaskName {
taskID: String!
name: String!
}
extend type Mutation {
createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist!
deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload!
updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist!
createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem!
updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem!
setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem!
deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): DeleteTaskChecklistItemPayload!
}
input CreateTaskChecklist {
taskID: UUID!
name: String!
position: Float!
}
type DeleteTaskChecklistItemPayload {
ok: Boolean!
taskChecklistItem: TaskChecklistItem!
}
input CreateTaskChecklistItem {
taskChecklistID: UUID!
name: String!
position: Float!
}
input SetTaskChecklistItemComplete {
taskChecklistItemID: UUID!
complete: Boolean!
}
input DeleteTaskChecklistItem {
taskChecklistItemID: UUID!
}
input UpdateTaskChecklistItemName {
taskChecklistItemID: UUID!
name: String!
}
input UpdateTaskChecklistName {
taskChecklistID: UUID!
name: String!
}
input DeleteTaskChecklist {
taskChecklistID: UUID!
}
type DeleteTaskChecklistPayload {
ok: Boolean!
taskChecklist: TaskChecklist!
}
extend type Mutation {
createTaskGroup(input: NewTaskGroup!): TaskGroup!
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
updateTaskGroupName(input: UpdateTaskGroupName!): TaskGroup!
deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload!
}
input NewTaskGroupLocation {
taskGroupID: UUID!
position: Float!
}
input UpdateTaskGroupName {
taskGroupID: UUID!
name: String!
}
input DeleteTaskGroupInput {
taskGroupID: UUID!
}
type DeleteTaskGroupPayload {
ok: Boolean!
affectedRows: Int!
taskGroup: TaskGroup!
}
input NewTaskGroup {
projectID: String!
name: String!
position: Float!
}
input AddTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
input RemoveTaskLabelInput {
taskLabelID: UUID!
}
input ToggleTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
type ToggleTaskLabelPayload {
active: Boolean!
task: Task!
}
extend type Mutation {
addTaskLabel(input: AddTaskLabelInput): Task!
removeTaskLabel(input: RemoveTaskLabelInput): Task!
toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload!
}
extend type Mutation {
deleteTeam(input: DeleteTeam!): DeleteTeamPayload!
createTeam(input: NewTeam!): Team!
}
input NewTeam {
name: String!
organizationID: UUID!
}
input DeleteTeam {
teamID: UUID!
}
type DeleteTeamPayload {
ok: Boolean!
team: Team!
projects: [Project!]!
}
extend type Mutation {
setTeamOwner(input: SetTeamOwner!): SetTeamOwnerPayload!
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
updateTeamMemberRole(input: UpdateTeamMemberRole!): UpdateTeamMemberRolePayload!
deleteTeamMember(input: DeleteTeamMember!): DeleteTeamMemberPayload!
}
input DeleteTeamMember {
teamID: UUID!
userID: UUID!
}
type DeleteTeamMemberPayload {
teamID: UUID!
userID: UUID!
}
input CreateTeamMember {
userID: UUID!
teamID: UUID!
}
type CreateTeamMemberPayload {
team: Team!
teamMember: Member!
}
input UpdateTeamMemberRole {
teamID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateTeamMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetTeamOwner {
teamID: UUID!
userID: UUID!
}
type SetTeamOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}
extend type Mutation {
createRefreshToken(input: NewRefreshToken!): RefreshToken!
createUserAccount(input: NewUserAccount!): UserAccount!
deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload!
logoutUser(input: LogoutUser!): Boolean!
clearProfileAvatar: UserAccount!
}
input NewRefreshToken {
userId: String!
}
input NewUserAccount {
username: String!
email: String!
fullName: String!
initials: String!
password: String!
roleCode: String!
}
input LogoutUser {
userID: String!
}
input DeleteUserAccount {
userID: UUID!
}
type DeleteUserAccountPayload {
ok: Boolean!
userAccount: UserAccount!
}

View File

@ -0,0 +1,560 @@
scalar Time
scalar UUID
scalar Upload
enum RoleCode {
owner
admin
member
observer
}
type ProjectLabel {
id: ID!
createdDate: Time!
labelColor: LabelColor!
name: String
}
type LabelColor {
id: ID!
name: String!
position: Float!
colorHex: String!
}
type TaskLabel {
id: ID!
projectLabel: ProjectLabel!
assignedDate: Time!
}
type ProfileIcon {
url: String
initials: String
bgColor: String
}
type Member {
id: ID!
role: Role!
fullName: String!
username: String!
profileIcon: ProfileIcon!
}
type RefreshToken {
id: ID!
userId: UUID!
expiresAt: Time!
createdAt: Time!
}
type Role {
code: String!
name: String!
}
type UserAccount {
id: ID!
email: String!
createdAt: Time!
fullName: String!
initials: String!
role: Role!
username: String!
profileIcon: ProfileIcon!
}
type Team {
id: ID!
createdAt: Time!
name: String!
members: [Member!]!
}
type Project {
id: ID!
createdAt: Time!
name: String!
team: Team!
owner: Member!
taskGroups: [TaskGroup!]!
members: [Member!]!
labels: [ProjectLabel!]!
}
type TaskGroup {
id: ID!
projectID: String!
createdAt: Time!
name: String!
position: Float!
tasks: [Task!]!
}
type ChecklistBadge {
complete: Int!
total: Int!
}
type TaskBadges {
checklist: ChecklistBadge
}
type Task {
id: ID!
taskGroup: TaskGroup!
createdAt: Time!
name: String!
position: Float!
description: String
dueDate: Time
complete: Boolean!
assigned: [Member!]!
labels: [TaskLabel!]!
checklists: [TaskChecklist!]!
badges: TaskBadges!
}
input ProjectsFilter {
teamID: UUID
}
input FindUser {
userId: String!
}
input FindProject {
projectId: String!
}
input FindTask {
taskID: UUID!
}
type Organization {
id: ID!
name: String!
}
input ProjectsFilter {
teamID: UUID
}
input FindUser {
userId: String!
}
input FindProject {
projectId: String!
}
input FindTask {
taskID: UUID!
}
type Organization {
id: ID!
name: String!
}
input FindTeam {
teamID: UUID!
}
type Query {
organizations: [Organization!]!
users: [UserAccount!]!
findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): Project!
findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team!
teams: [Team!]!
labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]!
me: UserAccount!
}
type Mutation {}
extend type Mutation {
createTask(input: NewTask!): Task!
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
updateTaskName(input: UpdateTaskName!): Task!
setTaskComplete(input: SetTaskComplete!): Task!
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
assignTask(input: AssignTaskInput): Task!
unassignTask(input: UnassignTaskInput): Task!
}
input NewRefreshToken {
userId: String!
}
input NewUserAccount {
username: String!
email: String!
fullName: String!
initials: String!
password: String!
roleCode: String!
}
input NewTeam {
name: String!
organizationID: UUID!
}
input NewProject {
userID: UUID!
teamID: UUID!
name: String!
}
input NewTaskGroup {
projectID: String!
name: String!
position: Float!
}
input LogoutUser {
userID: String!
}
input NewTask {
taskGroupID: String!
name: String!
position: Float!
}
input NewTaskLocation {
taskID: UUID!
taskGroupID: UUID!
position: Float!
}
input DeleteTaskInput {
taskID: String!
}
type DeleteTaskPayload {
taskID: String!
}
input UpdateTaskName {
taskID: String!
name: String!
}
input NewTaskGroupLocation {
taskGroupID: UUID!
position: Float!
}
input DeleteTaskGroupInput {
taskGroupID: UUID!
}
type DeleteTaskGroupPayload {
ok: Boolean!
affectedRows: Int!
taskGroup: TaskGroup!
}
type DeleteTaskChecklistItemPayload {
ok: Boolean!
taskChecklistItem: TaskChecklistItem!
}
type TaskChecklistItem {
id: ID!
name: String!
taskChecklistID: UUID!
complete: Boolean!
position: Float!
dueDate: Time!
}
type TaskChecklist {
id: ID!
name: String!
position: Float!
items: [TaskChecklistItem!]!
}
input AssignTaskInput {
taskID: UUID!
userID: UUID!
}
input UnassignTaskInput {
taskID: UUID!
userID: UUID!
}
input UpdateTaskDescriptionInput {
taskID: UUID!
description: String!
}
input AddTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
input RemoveTaskLabelInput {
taskLabelID: UUID!
}
input NewProjectLabel {
projectID: UUID!
labelColorID: UUID!
name: String
}
input DeleteProjectLabel {
projectLabelID: UUID!
}
input UpdateProjectLabelName {
projectLabelID: UUID!
name: String!
}
input UpdateProjectLabel {
projectLabelID: UUID!
labelColorID: UUID!
name: String!
}
input UpdateProjectLabelColor {
projectLabelID: UUID!
labelColorID: UUID!
}
input ToggleTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
type ToggleTaskLabelPayload {
active: Boolean!
task: Task!
}
input UpdateProjectName {
projectID: UUID!
name: String!
}
type UpdateTaskLocationPayload {
previousTaskGroupID: UUID!
task: Task!
}
input UpdateTaskGroupName {
taskGroupID: UUID!
name: String!
}
input UpdateTaskDueDate {
taskID: UUID!
dueDate: Time
}
input SetTaskComplete {
taskID: UUID!
complete: Boolean!
}
input CreateTaskChecklist {
taskID: UUID!
name: String!
position: Float!
}
input CreateTaskChecklistItem {
taskChecklistID: UUID!
name: String!
position: Float!
}
input SetTaskChecklistItemComplete {
taskChecklistItemID: UUID!
complete: Boolean!
}
input DeleteTaskChecklistItem {
taskChecklistItemID: UUID!
}
input UpdateTaskChecklistItemName {
taskChecklistItemID: UUID!
name: String!
}
input CreateTeamMember {
userID: UUID!
teamID: UUID!
}
type CreateTeamMemberPayload {
team: Team!
teamMember: Member!
}
input DeleteProject {
projectID: UUID!
}
type DeleteProjectPayload {
ok: Boolean!
project: Project!
}
input DeleteTeam {
teamID: UUID!
}
type DeleteTeamPayload {
ok: Boolean!
team: Team!
projects: [Project!]!
}
input DeleteUserAccount {
userID: UUID!
}
type DeleteUserAccountPayload {
ok: Boolean!
userAccount: UserAccount!
}
input UpdateTaskChecklistName {
taskChecklistID: UUID!
name: String!
}
input DeleteTaskChecklist {
taskChecklistID: UUID!
}
type DeleteTaskChecklistPayload {
ok: Boolean!
taskChecklist: TaskChecklist!
}
input CreateProjectMember {
projectID: UUID!
userID: UUID!
}
type CreateProjectMemberPayload {
ok: Boolean!
member: Member!
}
input DeleteProjectMember {
projectID: UUID!
userID: UUID!
}
type DeleteProjectMemberPayload {
ok: Boolean!
member: Member!
projectID: UUID!
}
input UpdateProjectMemberRole {
projectID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateProjectMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetProjectOwner {
projectID: UUID!
ownerID: UUID!
}
type SetProjectOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}
input UpdateTeamMemberRole {
teamID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateTeamMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetTeamOwner {
teamID: UUID!
userID: UUID!
}
type SetTeamOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}
type Mutation {
createRefreshToken(input: NewRefreshToken!): RefreshToken!
createUserAccount(input: NewUserAccount!): UserAccount!
deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload!
deleteTeam(input: DeleteTeam!): DeleteTeamPayload!
createTeam(input: NewTeam!): Team!
clearProfileAvatar: UserAccount!
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
updateTeamMemberRole(input: UpdateTeamMemberRole!): UpdateTeamMemberRolePayload!
setTeamOwner(input: SetTeamOwner!): SetTeamOwnerPayload!
createProject(input: NewProject!): Project!
deleteProject(input: DeleteProject!): DeleteProjectPayload!
updateProjectName(input: UpdateProjectName): Project!
createProjectMember(input: CreateProjectMember!): CreateProjectMemberPayload!
deleteProjectMember(input: DeleteProjectMember!): DeleteProjectMemberPayload!
updateProjectMemberRole(input: UpdateProjectMemberRole!): UpdateProjectMemberRolePayload!
setProjectOwner(input: SetProjectOwner!): SetProjectOwnerPayload!
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
deleteProjectLabel(input: DeleteProjectLabel!): ProjectLabel!
updateProjectLabel(input: UpdateProjectLabel!): ProjectLabel!
updateProjectLabelName(input: UpdateProjectLabelName!): ProjectLabel!
updateProjectLabelColor(input: UpdateProjectLabelColor!): ProjectLabel!
createTaskGroup(input: NewTaskGroup!): TaskGroup!
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
updateTaskGroupName(input: UpdateTaskGroupName!): TaskGroup!
deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload!
addTaskLabel(input: AddTaskLabelInput): Task!
removeTaskLabel(input: RemoveTaskLabelInput): Task!
toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload!
createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist!
deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload!
updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist!
createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem!
updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem!
setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem!
deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): DeleteTaskChecklistItemPayload!
logoutUser(input: LogoutUser!): Boolean!
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
scalar Time
scalar UUID
scalar Upload
enum RoleCode {
owner
admin
member
observer
}
type ProjectLabel {
id: ID!
createdDate: Time!
labelColor: LabelColor!
name: String
}
type LabelColor {
id: ID!
name: String!
position: Float!
colorHex: String!
}
type TaskLabel {
id: ID!
projectLabel: ProjectLabel!
assignedDate: Time!
}
type ProfileIcon {
url: String
initials: String
bgColor: String
}
type Member {
id: ID!
role: Role!
fullName: String!
username: String!
profileIcon: ProfileIcon!
}
type RefreshToken {
id: ID!
userId: UUID!
expiresAt: Time!
createdAt: Time!
}
type Role {
code: String!
name: String!
}
type UserAccount {
id: ID!
email: String!
createdAt: Time!
fullName: String!
initials: String!
role: Role!
username: String!
profileIcon: ProfileIcon!
}
type Team {
id: ID!
createdAt: Time!
name: String!
members: [Member!]!
}
type Project {
id: ID!
createdAt: Time!
name: String!
team: Team!
owner: Member!
taskGroups: [TaskGroup!]!
members: [Member!]!
labels: [ProjectLabel!]!
}
type TaskGroup {
id: ID!
projectID: String!
createdAt: Time!
name: String!
position: Float!
tasks: [Task!]!
}
type ChecklistBadge {
complete: Int!
total: Int!
}
type TaskBadges {
checklist: ChecklistBadge
}
type Task {
id: ID!
taskGroup: TaskGroup!
createdAt: Time!
name: String!
position: Float!
description: String
dueDate: Time
complete: Boolean!
assigned: [Member!]!
labels: [TaskLabel!]!
checklists: [TaskChecklist!]!
badges: TaskBadges!
}
type Organization {
id: ID!
name: String!
}
type TaskChecklistItem {
id: ID!
name: String!
taskChecklistID: UUID!
complete: Boolean!
position: Float!
dueDate: Time!
}
type TaskChecklist {
id: ID!
name: String!
position: Float!
items: [TaskChecklistItem!]!
}

View File

@ -0,0 +1,35 @@
type Query {
organizations: [Organization!]!
users: [UserAccount!]!
findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): Project!
findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team!
teams: [Team!]!
labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]!
me: UserAccount!
}
type Mutation
input ProjectsFilter {
teamID: UUID
}
input FindUser {
userId: String!
}
input FindProject {
projectId: String!
}
input FindTask {
taskID: UUID!
}
input FindTeam {
teamID: UUID!
}

View File

@ -0,0 +1,26 @@
extend type Mutation {
createProject(input: NewProject!): Project!
deleteProject(input: DeleteProject!): DeleteProjectPayload!
updateProjectName(input: UpdateProjectName): Project!
}
input NewProject {
userID: UUID!
teamID: UUID!
name: String!
}
input UpdateProjectName {
projectID: UUID!
name: String!
}
input DeleteProject {
projectID: UUID!
}
type DeleteProjectPayload {
ok: Boolean!
project: Project!
}

View File

@ -0,0 +1,33 @@
extend type Mutation {
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
deleteProjectLabel(input: DeleteProjectLabel!): ProjectLabel!
updateProjectLabel(input: UpdateProjectLabel!): ProjectLabel!
updateProjectLabelName(input: UpdateProjectLabelName!): ProjectLabel!
updateProjectLabelColor(input: UpdateProjectLabelColor!): ProjectLabel!
}
input NewProjectLabel {
projectID: UUID!
labelColorID: UUID!
name: String
}
input DeleteProjectLabel {
projectLabelID: UUID!
}
input UpdateProjectLabelName {
projectLabelID: UUID!
name: String!
}
input UpdateProjectLabel {
projectLabelID: UUID!
labelColorID: UUID!
name: String!
}
input UpdateProjectLabelColor {
projectLabelID: UUID!
labelColorID: UUID!
}

View File

@ -0,0 +1,48 @@
extend type Mutation {
createProjectMember(input: CreateProjectMember!): CreateProjectMemberPayload!
deleteProjectMember(input: DeleteProjectMember!): DeleteProjectMemberPayload!
updateProjectMemberRole(input: UpdateProjectMemberRole!): UpdateProjectMemberRolePayload!
setProjectOwner(input: SetProjectOwner!): SetProjectOwnerPayload!
}
input CreateProjectMember {
projectID: UUID!
userID: UUID!
}
type CreateProjectMemberPayload {
ok: Boolean!
member: Member!
}
input DeleteProjectMember {
projectID: UUID!
userID: UUID!
}
type DeleteProjectMemberPayload {
ok: Boolean!
member: Member!
projectID: UUID!
}
input UpdateProjectMemberRole {
projectID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateProjectMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetProjectOwner {
projectID: UUID!
ownerID: UUID!
}
type SetProjectOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}

View File

@ -0,0 +1,68 @@
extend type Mutation {
createTask(input: NewTask!): Task!
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
updateTaskName(input: UpdateTaskName!): Task!
setTaskComplete(input: SetTaskComplete!): Task!
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
assignTask(input: AssignTaskInput): Task!
unassignTask(input: UnassignTaskInput): Task!
}
input NewTask {
taskGroupID: String!
name: String!
position: Float!
}
input AssignTaskInput {
taskID: UUID!
userID: UUID!
}
input UnassignTaskInput {
taskID: UUID!
userID: UUID!
}
input UpdateTaskDescriptionInput {
taskID: UUID!
description: String!
}
type UpdateTaskLocationPayload {
previousTaskGroupID: UUID!
task: Task!
}
input UpdateTaskDueDate {
taskID: UUID!
dueDate: Time
}
input SetTaskComplete {
taskID: UUID!
complete: Boolean!
}
input NewTaskLocation {
taskID: UUID!
taskGroupID: UUID!
position: Float!
}
input DeleteTaskInput {
taskID: String!
}
type DeleteTaskPayload {
taskID: String!
}
input UpdateTaskName {
taskID: String!
name: String!
}

View File

@ -0,0 +1,52 @@
extend type Mutation {
createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist!
deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload!
updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist!
createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem!
updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem!
setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem!
deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): DeleteTaskChecklistItemPayload!
}
input CreateTaskChecklist {
taskID: UUID!
name: String!
position: Float!
}
type DeleteTaskChecklistItemPayload {
ok: Boolean!
taskChecklistItem: TaskChecklistItem!
}
input CreateTaskChecklistItem {
taskChecklistID: UUID!
name: String!
position: Float!
}
input SetTaskChecklistItemComplete {
taskChecklistItemID: UUID!
complete: Boolean!
}
input DeleteTaskChecklistItem {
taskChecklistItemID: UUID!
}
input UpdateTaskChecklistItemName {
taskChecklistItemID: UUID!
name: String!
}
input UpdateTaskChecklistName {
taskChecklistID: UUID!
name: String!
}
input DeleteTaskChecklist {
taskChecklistID: UUID!
}
type DeleteTaskChecklistPayload {
ok: Boolean!
taskChecklist: TaskChecklist!
}

View File

@ -0,0 +1,32 @@
extend type Mutation {
createTaskGroup(input: NewTaskGroup!): TaskGroup!
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
updateTaskGroupName(input: UpdateTaskGroupName!): TaskGroup!
deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload!
}
input NewTaskGroupLocation {
taskGroupID: UUID!
position: Float!
}
input UpdateTaskGroupName {
taskGroupID: UUID!
name: String!
}
input DeleteTaskGroupInput {
taskGroupID: UUID!
}
type DeleteTaskGroupPayload {
ok: Boolean!
affectedRows: Int!
taskGroup: TaskGroup!
}
input NewTaskGroup {
projectID: String!
name: String!
position: Float!
}

View File

@ -0,0 +1,22 @@
input AddTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
input RemoveTaskLabelInput {
taskLabelID: UUID!
}
input ToggleTaskLabelInput {
taskID: UUID!
projectLabelID: UUID!
}
type ToggleTaskLabelPayload {
active: Boolean!
task: Task!
}
extend type Mutation {
addTaskLabel(input: AddTaskLabelInput): Task!
removeTaskLabel(input: RemoveTaskLabelInput): Task!
toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload!
}

View File

@ -0,0 +1,19 @@
extend type Mutation {
deleteTeam(input: DeleteTeam!): DeleteTeamPayload!
createTeam(input: NewTeam!): Team!
}
input NewTeam {
name: String!
organizationID: UUID!
}
input DeleteTeam {
teamID: UUID!
}
type DeleteTeamPayload {
ok: Boolean!
team: Team!
projects: [Project!]!
}

View File

@ -0,0 +1,48 @@
extend type Mutation {
setTeamOwner(input: SetTeamOwner!): SetTeamOwnerPayload!
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
updateTeamMemberRole(input: UpdateTeamMemberRole!): UpdateTeamMemberRolePayload!
deleteTeamMember(input: DeleteTeamMember!): DeleteTeamMemberPayload!
}
input DeleteTeamMember {
teamID: UUID!
userID: UUID!
}
type DeleteTeamMemberPayload {
teamID: UUID!
userID: UUID!
}
input CreateTeamMember {
userID: UUID!
teamID: UUID!
}
type CreateTeamMemberPayload {
team: Team!
teamMember: Member!
}
input UpdateTeamMemberRole {
teamID: UUID!
userID: UUID!
roleCode: RoleCode!
}
type UpdateTeamMemberRolePayload {
ok: Boolean!
member: Member!
}
input SetTeamOwner {
teamID: UUID!
userID: UUID!
}
type SetTeamOwnerPayload {
ok: Boolean!
prevOwner: Member!
newOwner: Member!
}

View File

@ -0,0 +1,33 @@
extend type Mutation {
createRefreshToken(input: NewRefreshToken!): RefreshToken!
createUserAccount(input: NewUserAccount!): UserAccount!
deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload!
logoutUser(input: LogoutUser!): Boolean!
clearProfileAvatar: UserAccount!
}
input NewRefreshToken {
userId: String!
}
input NewUserAccount {
username: String!
email: String!
fullName: String!
initials: String!
password: String!
roleCode: String!
}
input LogoutUser {
userID: String!
}
input DeleteUserAccount {
userID: UUID!
}
type DeleteUserAccountPayload {
ok: Boolean!
userAccount: UserAccount!
}

View File

@ -0,0 +1,92 @@
package logger
import (
"fmt"
"net/http"
"time"
"github.com/go-chi/chi/middleware"
"github.com/sirupsen/logrus"
)
// StructuredLogger is a simple, but powerful implementation of a custom structured
// logger backed on logrus. I encourage users to copy it, adapt it and make it their
// own. Also take a look at https://github.com/pressly/lg for a dedicated pkg based
// on this work, designed for context-based http routers.
func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
return middleware.RequestLogger(&StructuredLogger{logger})
}
type StructuredLogger struct {
Logger *logrus.Logger
}
func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
logFields := logrus.Fields{}
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
logFields["req_id"] = reqID
}
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
logFields["http_scheme"] = scheme
logFields["http_proto"] = r.Proto
logFields["http_method"] = r.Method
logFields["remote_addr"] = r.RemoteAddr
logFields["user_agent"] = r.UserAgent()
logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
entry.Logger = entry.Logger.WithFields(logFields)
return entry
}
type StructuredLoggerEntry struct {
Logger logrus.FieldLogger
}
func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"resp_status": status, "resp_bytes_length": bytes,
"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
})
l.Logger.Debugln("request complete")
}
func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"stack": string(stack),
"panic": fmt.Sprintf("%+v", v),
})
}
// Helper methods used by the application to get the request-scoped
// logger entry and set additional fields between handlers.
//
// This is a useful pattern to use to set state on the entry as it
// passes through the handler chain, which at any point can be logged
// with a call to .Print(), .Info(), etc.
func GetLogEntry(r *http.Request) logrus.FieldLogger {
entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
return entry.Logger
}
func LogEntrySetField(r *http.Request, key string, value interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithField(key, value)
}
}
func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithFields(fields)
}
}

166
api/internal/route/auth.go Normal file
View File

@ -0,0 +1,166 @@
package route
import (
"encoding/json"
"net/http"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/go-chi/chi"
"github.com/google/uuid"
"github.com/jordanknott/project-citadel/api/internal/auth"
"github.com/jordanknott/project-citadel/api/internal/db"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
)
var jwtKey = []byte("citadel_test_key")
type authResource struct{}
type AccessTokenClaims struct {
UserID string `json:"userId"`
jwt.StandardClaims
}
type RefreshTokenClaims struct {
UserID string `json:"userId"`
jwt.StandardClaims
}
type LoginRequestData struct {
Username string
Password string
}
type LoginResponseData struct {
AccessToken string `json:"accessToken"`
}
type LogoutResponseData struct {
Status string `json:"status"`
}
type RefreshTokenResponseData struct {
AccessToken string `json:"accessToken"`
}
type AvatarUploadResponseData struct {
UserID string `json:"userID"`
URL string `json:"url"`
}
func (h *CitadelHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("refreshToken")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
refreshTokenID := uuid.MustParse(c.Value)
token, err := h.repo.GetRefreshTokenByID(r.Context(), refreshTokenID)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
refreshCreatedAt := time.Now().UTC()
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), db.CreateRefreshTokenParams{token.UserID, refreshCreatedAt, refreshExpiresAt})
err = h.repo.DeleteRefreshTokenByID(r.Context(), token.TokenID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
accessTokenString, err := auth.NewAccessToken(token.UserID.String())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
w.Header().Set("Content-type", "application/json")
http.SetCookie(w, &http.Cookie{
Name: "refreshToken",
Value: refreshTokenString.TokenID.String(),
Expires: refreshExpiresAt,
HttpOnly: true,
})
json.NewEncoder(w).Encode(LoginResponseData{AccessToken: accessTokenString})
}
func (h *CitadelHandler) LogoutHandler(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("refreshToken")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
refreshTokenID := uuid.MustParse(c.Value)
err = h.repo.DeleteRefreshTokenByID(r.Context(), refreshTokenID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(LogoutResponseData{Status: "success"})
}
func (h *CitadelHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
var requestData LoginRequestData
err := json.NewDecoder(r.Body).Decode(&requestData)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.Debug("bad request body")
return
}
user, err := h.repo.GetUserAccountByUsername(r.Context(), requestData.Username)
if err != nil {
log.WithFields(log.Fields{
"username": requestData.Username,
}).Warn("user account not found")
w.WriteHeader(http.StatusUnauthorized)
return
}
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(requestData.Password))
if err != nil {
log.WithFields(log.Fields{
"password": requestData.Password,
"password_hash": user.PasswordHash,
}).Warn("password incorrect")
w.WriteHeader(http.StatusUnauthorized)
return
}
refreshCreatedAt := time.Now().UTC()
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), db.CreateRefreshTokenParams{user.UserID, refreshCreatedAt, refreshExpiresAt})
accessTokenString, err := auth.NewAccessToken(user.UserID.String())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
w.Header().Set("Content-type", "application/json")
http.SetCookie(w, &http.Cookie{
Name: "refreshToken",
Value: refreshTokenString.TokenID.String(),
Expires: refreshExpiresAt,
HttpOnly: true,
})
json.NewEncoder(w).Encode(LoginResponseData{accessTokenString})
}
func (rs authResource) Routes(citadelHandler CitadelHandler) chi.Router {
r := chi.NewRouter()
r.Post("/login", citadelHandler.LoginHandler)
r.Post("/refresh_token", citadelHandler.RefreshTokenHandler)
r.Post("/logout", citadelHandler.LogoutHandler)
return r
}

View File

@ -0,0 +1,52 @@
package route
import (
"database/sql"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/jordanknott/project-citadel/api/internal/db"
)
func (h *CitadelHandler) ProfileImageUpload(w http.ResponseWriter, r *http.Request) {
log.Info("preparing to upload file")
userID, ok := r.Context().Value("userID").(uuid.UUID)
if !ok {
log.Error("not a valid uuid")
w.WriteHeader(http.StatusInternalServerError)
return
}
// Parse our multipart form, 10 << 20 specifies a maximum
// upload of 10 MB files.
r.ParseMultipartForm(10 << 20)
file, handler, err := r.FormFile("file")
if err != nil {
log.WithError(err).Error("issue while uploading file")
return
}
defer file.Close()
log.WithFields(log.Fields{"filename": handler.Filename, "size": handler.Size, "header": handler.Header}).Info("file metadata")
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
log.WithError(err).Error("while reading file")
return
}
err = ioutil.WriteFile("uploads/"+handler.Filename, fileBytes, 0644)
if err != nil {
log.WithError(err).Error("while reading file")
return
}
h.repo.UpdateUserAccountProfileAvatarURL(r.Context(), db.UpdateUserAccountProfileAvatarURLParams{UserID: userID, ProfileAvatarUrl: sql.NullString{String: "http://localhost:3333/uploads/" + handler.Filename, Valid: true}})
// return that we have successfully uploaded our file!
log.Info("file uploaded")
json.NewEncoder(w).Encode(AvatarUploadResponseData{URL: "http://localhost:3333/uploads/" + handler.Filename, UserID: userID.String()})
}

View File

@ -0,0 +1,53 @@
package route
import (
"context"
"net/http"
"strings"
"github.com/google/uuid"
"github.com/jordanknott/project-citadel/api/internal/auth"
log "github.com/sirupsen/logrus"
)
func AuthenticationMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bearerTokenRaw := r.Header.Get("Authorization")
splitToken := strings.Split(bearerTokenRaw, "Bearer")
if len(splitToken) != 2 {
w.WriteHeader(http.StatusBadRequest)
return
}
accessTokenString := strings.TrimSpace(splitToken[1])
accessClaims, err := auth.ValidateAccessToken(accessTokenString)
if err != nil {
if _, ok := err.(*auth.ErrExpiredToken); ok {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{
"data": {},
"errors": [
{
"extensions": {
"code": "UNAUTHENTICATED"
}
}
]
}`))
return
}
log.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
}
userID, err := uuid.Parse(accessClaims.UserID)
if err != nil {
log.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
}
ctx := context.WithValue(r.Context(), "userID", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

View File

@ -0,0 +1,66 @@
package route
import (
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"github.com/jmoiron/sqlx"
log "github.com/sirupsen/logrus"
"github.com/jordanknott/project-citadel/api/internal/db"
"github.com/jordanknott/project-citadel/api/internal/graph"
"github.com/jordanknott/project-citadel/api/internal/logger"
)
type CitadelHandler struct {
repo db.Repository
}
func NewRouter(dbConnection *sqlx.DB) (chi.Router, error) {
formatter := new(log.TextFormatter)
formatter.TimestampFormat = "02-01-2006 15:04:05"
formatter.FullTimestamp = true
routerLogger := log.New()
routerLogger.SetLevel(log.InfoLevel)
routerLogger.Formatter = formatter
r := chi.NewRouter()
cors := cors.New(cors.Options{
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
AllowedOrigins: []string{"*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "Cookie"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300, // Maximum value not ignored by any of major browsers
})
r.Use(cors.Handler)
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(logger.NewStructuredLogger(routerLogger))
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(60 * time.Second))
repository := db.NewRepository(dbConnection)
citadelHandler := CitadelHandler{*repository}
var imgServer = http.FileServer(http.Dir("./uploads/"))
r.Group(func(mux chi.Router) {
mux.Mount("/auth", authResource{}.Routes(citadelHandler))
mux.Handle("/__graphql", graph.NewPlaygroundHandler("/graphql"))
mux.Mount("/uploads/", http.StripPrefix("/uploads/", imgServer))
})
r.Group(func(mux chi.Router) {
mux.Use(AuthenticationMiddleware)
mux.Post("/users/me/avatar", citadelHandler.ProfileImageUpload)
mux.Handle("/graphql", graph.NewHandler(*repository))
})
return r, nil
}