initial commit
This commit is contained in:
6217
api/graph/generated.go
Normal file
6217
api/graph/generated.go
Normal file
File diff suppressed because it is too large
Load Diff
22
api/graph/graph.go
Normal file
22
api/graph/graph.go
Normal file
@ -0,0 +1,22 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/handler"
|
||||
"github.com/jordanknott/project-citadel/api/pg"
|
||||
)
|
||||
|
||||
// NewHandler returns a new graphql endpoint handler.
|
||||
func NewHandler(repo pg.Repository) http.Handler {
|
||||
return handler.GraphQL(NewExecutableSchema(Config{
|
||||
Resolvers: &Resolver{
|
||||
Repository: repo,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// NewPlaygroundHandler returns a new GraphQL Playground handler.
|
||||
func NewPlaygroundHandler(endpoint string) http.Handler {
|
||||
return handler.Playground("GraphQL Playground", endpoint)
|
||||
}
|
75
api/graph/models_gen.go
Normal file
75
api/graph/models_gen.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
||||
|
||||
package graph
|
||||
|
||||
type DeleteTaskInput struct {
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
type DeleteTaskPayload struct {
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
type FindProject struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
type FindUser struct {
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
type LogoutUser struct {
|
||||
UserID string `json:"userID"`
|
||||
}
|
||||
|
||||
type NewOrganization struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type NewProject struct {
|
||||
TeamID string `json:"teamID"`
|
||||
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 NewTaskLocation struct {
|
||||
TaskID string `json:"taskID"`
|
||||
TaskGroupID string `json:"taskGroupID"`
|
||||
Position float64 `json:"position"`
|
||||
}
|
||||
|
||||
type NewTeam struct {
|
||||
Name string `json:"name"`
|
||||
OrganizationID string `json:"organizationID"`
|
||||
}
|
||||
|
||||
type NewUserAccount struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type ProjectsFilter struct {
|
||||
TeamID *string `json:"teamID"`
|
||||
}
|
||||
|
||||
type UpdateTaskName struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Name string `json:"name"`
|
||||
}
|
13
api/graph/resolver.go
Normal file
13
api/graph/resolver.go
Normal file
@ -0,0 +1,13 @@
|
||||
//go:generate go run github.com/99designs/gqlgen
|
||||
package graph
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jordanknott/project-citadel/api/pg"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
Repository pg.Repository
|
||||
mu sync.Mutex
|
||||
}
|
23
api/graph/scalars.go
Normal file
23
api/graph/scalars.go
Normal 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")
|
||||
}
|
151
api/graph/schema.graphqls
Normal file
151
api/graph/schema.graphqls
Normal file
@ -0,0 +1,151 @@
|
||||
scalar Time
|
||||
|
||||
scalar UUID
|
||||
|
||||
type RefreshToken {
|
||||
tokenId: ID!
|
||||
userId: UUID!
|
||||
expiresAt: Time!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type UserAccount {
|
||||
userID: ID!
|
||||
email: String!
|
||||
createdAt: Time!
|
||||
displayName: String!
|
||||
username: String!
|
||||
}
|
||||
|
||||
type Organization {
|
||||
organizationID: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
teams: [Team!]!
|
||||
}
|
||||
|
||||
type Team {
|
||||
teamID: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
projects: [Project!]!
|
||||
}
|
||||
|
||||
type Project {
|
||||
projectID: ID!
|
||||
teamID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
taskGroups: [TaskGroup!]!
|
||||
}
|
||||
|
||||
type TaskGroup {
|
||||
taskGroupID: ID!
|
||||
projectID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
position: Float!
|
||||
tasks: [Task!]!
|
||||
}
|
||||
|
||||
type Task {
|
||||
taskID: ID!
|
||||
taskGroupID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
position: Float!
|
||||
}
|
||||
|
||||
input ProjectsFilter {
|
||||
teamID: String
|
||||
}
|
||||
|
||||
input FindUser {
|
||||
userId: String!
|
||||
}
|
||||
|
||||
input FindProject {
|
||||
projectId: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
organizations: [Organization!]!
|
||||
users: [UserAccount!]!
|
||||
findUser(input: FindUser!): UserAccount!
|
||||
findProject(input: FindProject!): Project!
|
||||
teams: [Team!]!
|
||||
projects(input: ProjectsFilter): [Project!]!
|
||||
taskGroups: [TaskGroup!]!
|
||||
}
|
||||
|
||||
input NewRefreshToken {
|
||||
userId: String!
|
||||
}
|
||||
|
||||
input NewUserAccount {
|
||||
username: String!
|
||||
email: String!
|
||||
displayName: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
input NewTeam {
|
||||
name: String!
|
||||
organizationID: String!
|
||||
}
|
||||
|
||||
input NewProject {
|
||||
teamID: String!
|
||||
name: String!
|
||||
}
|
||||
|
||||
input NewTaskGroup {
|
||||
projectID: String!
|
||||
name: String!
|
||||
position: Float!
|
||||
}
|
||||
|
||||
input NewOrganization {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input LogoutUser {
|
||||
userID: String!
|
||||
}
|
||||
input NewTask {
|
||||
taskGroupID: String!
|
||||
name: String!
|
||||
position: Float!
|
||||
}
|
||||
input NewTaskLocation {
|
||||
taskID: String!
|
||||
taskGroupID: String!
|
||||
position: Float!
|
||||
}
|
||||
|
||||
input DeleteTaskInput {
|
||||
taskID: String!
|
||||
}
|
||||
|
||||
type DeleteTaskPayload {
|
||||
taskID: String!
|
||||
}
|
||||
|
||||
input UpdateTaskName {
|
||||
taskID: String!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||
createUserAccount(input: NewUserAccount!): UserAccount!
|
||||
createOrganization(input: NewOrganization!): Organization!
|
||||
createTeam(input: NewTeam!): Team!
|
||||
createProject(input: NewProject!): Project!
|
||||
createTaskGroup(input: NewTaskGroup!): TaskGroup!
|
||||
createTask(input: NewTask!): Task!
|
||||
updateTaskLocation(input: NewTaskLocation!): Task!
|
||||
logoutUser(input: LogoutUser!): Boolean!
|
||||
updateTaskName(input: UpdateTaskName!): Task!
|
||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||
}
|
232
api/graph/schema.resolvers.go
Normal file
232
api/graph/schema.resolvers.go
Normal file
@ -0,0 +1,232 @@
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jordanknott/project-citadel/api/pg"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
)
|
||||
|
||||
func (r *mutationResolver) CreateRefreshToken(ctx context.Context, input NewRefreshToken) (*pg.RefreshToken, error) {
|
||||
userID := uuid.MustParse("0183d9ab-d0ed-4c9b-a3df-77a0cdd93dca")
|
||||
refreshCreatedAt := time.Now().UTC()
|
||||
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
|
||||
refreshToken, err := r.Repository.CreateRefreshToken(ctx, pg.CreateRefreshTokenParams{userID, refreshCreatedAt, refreshExpiresAt})
|
||||
return &refreshToken, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserAccount) (*pg.UserAccount, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
userAccount, err := r.Repository.CreateUserAccount(ctx, pg.CreateUserAccountParams{input.Username, input.Email, input.DisplayName, createdAt, input.Password})
|
||||
return &userAccount, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateOrganization(ctx context.Context, input NewOrganization) (*pg.Organization, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
organization, err := r.Repository.CreateOrganization(ctx, pg.CreateOrganizationParams{createdAt, input.Name})
|
||||
return &organization, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) {
|
||||
organizationID, err := uuid.Parse(input.OrganizationID)
|
||||
if err != nil {
|
||||
return &pg.Team{}, err
|
||||
}
|
||||
createdAt := time.Now().UTC()
|
||||
team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{organizationID, createdAt, input.Name})
|
||||
return &team, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
teamID, err := uuid.Parse(input.TeamID)
|
||||
if err != nil {
|
||||
return &pg.Project{}, err
|
||||
}
|
||||
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{teamID, createdAt, input.Name})
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*pg.TaskGroup, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
projectID, err := uuid.Parse(input.ProjectID)
|
||||
if err != nil {
|
||||
return &pg.TaskGroup{}, err
|
||||
}
|
||||
project, err := r.Repository.CreateTaskGroup(ctx,
|
||||
pg.CreateTaskGroupParams{projectID, createdAt, input.Name, input.Position})
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*pg.Task, error) {
|
||||
taskGroupID, err := uuid.Parse(input.TaskGroupID)
|
||||
createdAt := time.Now().UTC()
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
|
||||
task, err := r.Repository.CreateTask(ctx, pg.CreateTaskParams{taskGroupID, createdAt, input.Name, input.Position})
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*pg.Task, error) {
|
||||
taskID, err := uuid.Parse(input.TaskID)
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
taskGroupID, err := uuid.Parse(input.TaskGroupID)
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
task, err := r.Repository.UpdateTaskLocation(ctx, pg.UpdateTaskLocationParams{taskID, taskGroupID, input.Position})
|
||||
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) {
|
||||
userID, err := uuid.Parse(input.UserID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = r.Repository.DeleteRefreshTokenByUserID(ctx, userID)
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error) {
|
||||
taskID, err := uuid.Parse(input.TaskID)
|
||||
if err != nil {
|
||||
return &pg.Task{}, err
|
||||
}
|
||||
task, err := r.Repository.UpdateTaskName(ctx, pg.UpdateTaskNameParams{taskID, input.Name})
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) {
|
||||
taskID, err := uuid.Parse(input.TaskID)
|
||||
if err != nil {
|
||||
return &DeleteTaskPayload{}, err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"taskID": taskID.String(),
|
||||
}).Info("deleting task")
|
||||
err = r.Repository.DeleteTaskByID(ctx, taskID)
|
||||
if err != nil {
|
||||
return &DeleteTaskPayload{}, err
|
||||
}
|
||||
return &DeleteTaskPayload{taskID.String()}, nil
|
||||
}
|
||||
|
||||
func (r *organizationResolver) Teams(ctx context.Context, obj *pg.Organization) ([]pg.Team, error) {
|
||||
teams, err := r.Repository.GetTeamsForOrganization(ctx, obj.OrganizationID)
|
||||
return teams, err
|
||||
}
|
||||
|
||||
func (r *projectResolver) TeamID(ctx context.Context, obj *pg.Project) (string, error) {
|
||||
return obj.TeamID.String(), nil
|
||||
}
|
||||
|
||||
func (r *projectResolver) TaskGroups(ctx context.Context, obj *pg.Project) ([]pg.TaskGroup, error) {
|
||||
return r.Repository.GetTaskGroupsForProject(ctx, obj.ProjectID)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Organizations(ctx context.Context) ([]pg.Organization, error) {
|
||||
return r.Repository.GetAllOrganizations(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
|
||||
return r.Repository.GetAllUserAccounts(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) FindUser(ctx context.Context, input FindUser) (*pg.UserAccount, error) {
|
||||
userId, err := uuid.Parse(input.UserID)
|
||||
if err != nil {
|
||||
return &pg.UserAccount{}, err
|
||||
}
|
||||
account, err := r.Repository.GetUserAccountByID(ctx, userId)
|
||||
if err == sql.ErrNoRows {
|
||||
return &pg.UserAccount{}, &gqlerror.Error{
|
||||
Message: "User not found",
|
||||
Extensions: map[string]interface{}{
|
||||
"code": "10-404",
|
||||
},
|
||||
}
|
||||
}
|
||||
return &account, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*pg.Project, error) {
|
||||
projectID, err := uuid.Parse(input.ProjectID)
|
||||
if err != nil {
|
||||
return &pg.Project{}, err
|
||||
}
|
||||
project, err := r.Repository.GetProjectByID(ctx, projectID)
|
||||
if err == sql.ErrNoRows {
|
||||
return &pg.Project{}, &gqlerror.Error{
|
||||
Message: "Project not found",
|
||||
Extensions: map[string]interface{}{
|
||||
"code": "11-404",
|
||||
},
|
||||
}
|
||||
}
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) Teams(ctx context.Context) ([]pg.Team, error) {
|
||||
return r.Repository.GetAllTeams(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error) {
|
||||
if input != nil {
|
||||
teamID, err := uuid.Parse(*input.TeamID)
|
||||
if err != nil {
|
||||
return []pg.Project{}, err
|
||||
}
|
||||
return r.Repository.GetAllProjectsForTeam(ctx, teamID)
|
||||
}
|
||||
return r.Repository.GetAllProjects(ctx)
|
||||
}
|
||||
|
||||
func (r *queryResolver) TaskGroups(ctx context.Context) ([]pg.TaskGroup, error) {
|
||||
return r.Repository.GetAllTaskGroups(ctx)
|
||||
}
|
||||
|
||||
func (r *taskResolver) TaskGroupID(ctx context.Context, obj *pg.Task) (string, error) {
|
||||
return obj.TaskGroupID.String(), nil
|
||||
}
|
||||
|
||||
func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) {
|
||||
return obj.ProjectID.String(), nil
|
||||
}
|
||||
|
||||
func (r *taskGroupResolver) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.Task, error) {
|
||||
tasks, err := r.Repository.GetTasksForTaskGroupID(ctx, obj.TaskGroupID)
|
||||
return tasks, err
|
||||
}
|
||||
|
||||
func (r *teamResolver) Projects(ctx context.Context, obj *pg.Team) ([]pg.Project, error) {
|
||||
return r.Repository.GetAllProjectsForTeam(ctx, obj.TeamID)
|
||||
}
|
||||
|
||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||
func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{r} }
|
||||
func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} }
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
|
||||
func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} }
|
||||
func (r *Resolver) Team() TeamResolver { return &teamResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type projectResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
type taskGroupResolver struct{ *Resolver }
|
||||
type teamResolver struct{ *Resolver }
|
Reference in New Issue
Block a user