From 14c9425ee758da11991ee88f9bd20629d731aff9 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Wed, 13 May 2020 17:14:54 +0200 Subject: [PATCH] add item (missing upload) --- graph/resolvers/mutation.resolvers.go | 36 ++++++++++++ graph/resolvers/query.resolvers.go | 32 ++++++++++- graph/schema/item.gql | 57 +++++++++++++++++++ graph/schema/mutation.gql | 4 ++ graph/schema/query.gql | 17 ++++-- graph/schema/scalars.gql | 7 ++- .../20200406110301_create_table_item.sql | 3 +- models/issueitem.go | 1 + 8 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 graph/schema/item.gql diff --git a/graph/resolvers/mutation.resolvers.go b/graph/resolvers/mutation.resolvers.go index a0bd7fe..e1620bf 100644 --- a/graph/resolvers/mutation.resolvers.go +++ b/graph/resolvers/mutation.resolvers.go @@ -6,6 +6,7 @@ package resolvers import ( "context" "errors" + "fmt" "time" "git.aiterp.net/stufflog/server/graph/graphcore" @@ -123,6 +124,41 @@ func (r *mutationResolver) EditActivity(ctx context.Context, input graphcore.Act return activity, nil } +func (r *mutationResolver) CreateItem(ctx context.Context, input *graphcore.ItemCreateInput) (*models.Item, error) { + user := r.Auth.UserFromContext(ctx) + if user == nil { + return nil, xlerrors.PermissionDenied + } + + if input.Name == "" { + return nil, errors.New("name cannot be blank") + } + if input.Description == "" { + return nil, errors.New("name cannot be blank") + } + if len(input.Tags) == 0 { + return nil, errors.New("at least one tag is required") + } + + if input.Image != nil { + panic("todo: implement image upload") + } + + item := models.Item{ + Name: input.Name, + Description: input.Description, + Tags: input.Tags, + QuantityUnit: input.QuantityUnit, + ImageURL: nil, + } + + return r.Database.Items().Insert(ctx, item) +} + +func (r *mutationResolver) EditItem(ctx context.Context, input *graphcore.ItemEditInput) (*models.Item, error) { + panic(fmt.Errorf("not implemented")) +} + func (r *mutationResolver) CreateIssue(ctx context.Context, input graphcore.IssueCreateInput) (*models.Issue, error) { user := r.Auth.UserFromContext(ctx) if user == nil { diff --git a/graph/resolvers/query.resolvers.go b/graph/resolvers/query.resolvers.go index c50a658..5f4e72e 100644 --- a/graph/resolvers/query.resolvers.go +++ b/graph/resolvers/query.resolvers.go @@ -6,7 +6,6 @@ package resolvers import ( "context" "errors" - "git.aiterp.net/stufflog/server/graph/graphcore" "git.aiterp.net/stufflog/server/internal/xlerrors" "git.aiterp.net/stufflog/server/models" @@ -58,6 +57,37 @@ func (r *queryResolver) Issues(ctx context.Context, filter *models.IssueFilter) return issues, nil } +func (r *queryResolver) Item(ctx context.Context, id string) (*models.Item, error) { + user := r.Auth.UserFromContext(ctx) + if user == nil { + return nil, xlerrors.PermissionDenied + } + + return r.Database.Items().Find(ctx, id) +} + +func (r *queryResolver) Items(ctx context.Context, filter *models.ItemFilter) ([]*models.Item, error) { + user := r.Auth.UserFromContext(ctx) + if user == nil { + return nil, xlerrors.PermissionDenied + } + + if filter == nil { + filter = &models.ItemFilter{} + } + + return r.Database.Items().List(ctx, *filter) +} + +func (r *queryResolver) ItemTags(ctx context.Context) ([]string, error) { + user := r.Auth.UserFromContext(ctx) + if user == nil { + return nil, xlerrors.PermissionDenied + } + + return r.Database.Items().GetTags(ctx) +} + func (r *queryResolver) Project(ctx context.Context, id string) (*models.Project, error) { user := r.Auth.UserFromContext(ctx) if user == nil { diff --git a/graph/schema/item.gql b/graph/schema/item.gql new file mode 100644 index 0000000..f8921b0 --- /dev/null +++ b/graph/schema/item.gql @@ -0,0 +1,57 @@ +""" +An item that can be required for an issue. +""" +type Item { + "The item's unique ID." + id: String! + "Name of the item." + name: String! + "A description of the item." + description: String! + "Item tags." + tags: [String!]! + "Quantity unit. Usually absent, but otherwise most often 'g' or 'ml'" + quantityUnit: String + "URL for the image, if available." + imageUrl: String +} + +"Filter for the items query." +input ItemFilter { + "Get these item IDs. Mostly used internally." + itemIds: [String!] + "Limit to items with any of the following tags." + tags: [String!] +} + +"Input for the createItem mutation." +input ItemCreateInput { + "Put a name on it." + name: String! + "Describe it for me." + description: String! + "Add a tag or a few" + tags: [String!]! + "Optional: Quanity unit." + quantityUnit: String + "Optional: Upload an image." + image: Upload +} + +"Input for the editItem mutation." +input ItemEditInput { + "Update the name." + setName: String + "Update the description." + setDescription: String + "Add new tags. The tags are added after removeTag tags are removed." + addTags: [String] + "Remove existing tags. If a tag exists both here and in addTags, it will not be removed." + removeTags: [String] + "Update quantity unit." + setQuantityUnit: String + "Clear quantity unit." + clearQuantityUnit: Boolean + "Update the image URL with a new image." + updateImage: Upload +} \ No newline at end of file diff --git a/graph/schema/mutation.gql b/graph/schema/mutation.gql index c025592..11864fb 100644 --- a/graph/schema/mutation.gql +++ b/graph/schema/mutation.gql @@ -10,6 +10,10 @@ type Mutation { editActivity(input: ActivityEditInput!): Activity! # ITEM + "Create an item." + createItem(input: ItemCreateInput): Item! + "Edit an item." + editItem(input: ItemEditInput): Item! # ISSUE "Create a new issue." diff --git a/graph/schema/query.gql b/graph/schema/query.gql index 43dfbe1..f1073d3 100644 --- a/graph/schema/query.gql +++ b/graph/schema/query.gql @@ -1,14 +1,21 @@ type Query { - "Find issue" + "Find issue." issue(id: String!): Issue! - "List issues" + "List issues." issues(filter: IssueFilter): [Issue!]! - "Find project" + "Find item." + item(id: String!): Item! + "List items." + items(filter: ItemFilter): [Item!]! + "List item tags." + itemTags: [String!]! + + "Find project." project(id: String!): Project! - "List projects" + "List projects." projects(filter: ProjectFilter): [Project!]! - "Session checks the user session." + "Check the user session." session: User } \ No newline at end of file diff --git a/graph/schema/scalars.gql b/graph/schema/scalars.gql index d547f9a..65168aa 100644 --- a/graph/schema/scalars.gql +++ b/graph/schema/scalars.gql @@ -7,4 +7,9 @@ scalar Time Duration in milliseconds. It will output as an integer, but can be supplied as a float, integer or a string accepted by the Go time.Duration parser. """ -scalar Duration \ No newline at end of file +scalar Duration + +""" +A file upload. +""" +scalar Upload \ No newline at end of file diff --git a/migrations/mysql/20200406110301_create_table_item.sql b/migrations/mysql/20200406110301_create_table_item.sql index 968561b..1cea341 100644 --- a/migrations/mysql/20200406110301_create_table_item.sql +++ b/migrations/mysql/20200406110301_create_table_item.sql @@ -7,7 +7,8 @@ CREATE TABLE item ( quantity_unit VARCHAR(255), image_url VARCHAR(255), - FULLTEXT(name, description) + FULLTEXT(name, description), + INDEX(name) ); -- +goose StatementEnd diff --git a/models/issueitem.go b/models/issueitem.go index 1a2309d..0a7de91 100644 --- a/models/issueitem.go +++ b/models/issueitem.go @@ -6,4 +6,5 @@ type IssueItem struct { IssueID string `db:"issue_id"` ItemID string `db:"item_id"` Quantity int `db:"quantity"` + Acquired bool `db:"resolved"` }