From 90a3597602c05b05bf514c1f0598bb63e8664f74 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Tue, 12 Jun 2018 23:06:24 +0200 Subject: [PATCH] File model and api is almost complete - Added "Kind" field to File model - Added file query - Added editFile mutation - Added removeFile mutation --- model/file/file.go | 49 +++++++++++++++ resolver/file.go | 129 ++++++++++++++++++++++++++++++++++++++ schema/root.graphql | 11 ++++ schema/types/file.graphql | 43 +++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 resolver/file.go create mode 100644 schema/types/file.graphql diff --git a/model/file/file.go b/model/file/file.go index 6dded9a..63debcf 100644 --- a/model/file/file.go +++ b/model/file/file.go @@ -19,6 +19,7 @@ var fileCollection *mgo.Collection type File struct { ID string `bson:"_id" json:"id"` Time time.Time `bson:"time" json:"time"` + Kind string `bson:"kind" json:"kind"` Public bool `bson:"public" json:"public"` Name string `bson:"name" json:"name"` MimeType string `bson:"mimeType" json:"mimeType"` @@ -27,6 +28,53 @@ type File struct { URL string `bson:"url,omitempty" json:"url,omitempty"` } +// Edit edits the file, changing up to both the two mutable properties +func (file *File) Edit(name *string, public *bool) error { + changes := bson.M{} + changedFile := *file + + if name != nil && *name != file.Name { + changes["name"] = *name + changedFile.Name = *name + } + if public != nil && *public != file.Public { + changes["public"] = *public + changedFile.Public = *public + } + + if len(changes) == 0 { + return nil + } + + err := fileCollection.UpdateId(file.ID, bson.M{"$set": changes}) + if err != nil { + return err + } + + *file = changedFile + + return nil +} + +// Delete removes the file information from the database, and deletes the file. +func (file *File) Delete() error { + err := fileCollection.RemoveId(file.ID) + if err != nil { + return err + } + + if file.Kind == "upload" { + err = store.RemoveFile("files", file.ID) + if err != nil { + return err + } + } + + file.URL = "" + + return nil +} + // Upload adds a file to the space. func Upload(ctx context.Context, name, mimeType, author string, size int64, input io.Reader) (File, error) { if name == "" { @@ -47,6 +95,7 @@ func Upload(ctx context.Context, name, mimeType, author string, size int64, inpu file := File{ ID: id, + Kind: "upload", Time: time.Now(), Public: false, Author: author, diff --git a/resolver/file.go b/resolver/file.go new file mode 100644 index 0000000..e78e349 --- /dev/null +++ b/resolver/file.go @@ -0,0 +1,129 @@ +package resolver + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/file" +) + +// FileResolver for the File graphql type +type FileResolver struct{ F file.File } + +// FileArgs is an arg +type FileArgs struct { + ID string +} + +// File implements the file query +func (r *QueryResolver) File(ctx context.Context, args *FileArgs) (*FileResolver, error) { + file, err := file.FindID(args.ID) + if err != nil { + return nil, err + } + + return &FileResolver{F: file}, nil +} + +// FileEditInput is an input for the editFile mutation +type FileEditInput struct { + ID string + Name *string + Public *bool +} + +// EditFile resolves the editFile mutation +func (r *MutationResolver) EditFile(ctx context.Context, args *struct{ Input FileEditInput }) (*FileResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + file, err := file.FindID(args.Input.ID) + if err != nil { + return nil, err + } + if file.Author != user.ID && !user.Permitted("file.edit") { + return nil, ErrUnauthorized + } + + err = file.Edit(args.Input.Name, args.Input.Public) + if err != nil { + return nil, err + } + + return &FileResolver{F: file}, nil +} + +// RemoveFile resolves the removeFIle mutation +func (r *MutationResolver) RemoveFile(ctx context.Context, args *FileArgs) (*FileResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + file, err := file.FindID(args.ID) + if err != nil { + return nil, err + } + if file.Author != user.ID && !user.Permitted("file.remove") { + return nil, ErrUnauthorized + } + + err = file.Delete() + if err != nil { + return nil, err + } + + return &FileResolver{F: file}, nil +} + +// ID resolves File.id +func (f *FileResolver) ID() string { + return f.F.ID +} + +// Author resolves File.author +func (f *FileResolver) Author() string { + return f.F.Author +} + +// Kind resolves File.kind +func (f *FileResolver) Kind() string { + return f.F.Kind +} + +// Time resolves File.time +func (f *FileResolver) Time() string { + return f.F.Time.Format(time.RFC3339Nano) +} + +// Public resolves File.public +func (f *FileResolver) Public() bool { + return f.F.Public +} + +// Name resolves File.name +func (f *FileResolver) Name() string { + return f.F.Name +} + +// MimeType resolves File.mimeType +func (f *FileResolver) MimeType() string { + return f.F.MimeType +} + +// Size resolves File.size +func (f *FileResolver) Size() int32 { + return int32(f.F.Size) +} + +// URL resolves File.url +func (f *FileResolver) URL() *string { + if f.F.URL == "" { + return nil + } + + return &f.F.URL +} diff --git a/schema/root.graphql b/schema/root.graphql index 2f0f5c6..77df271 100644 --- a/schema/root.graphql +++ b/schema/root.graphql @@ -22,6 +22,10 @@ type Query { posts(ids: [String!]!): [Post!]! + # Find file by ID + file(id: String!): File + + # Find current session session: Session! } @@ -67,6 +71,13 @@ type Mutation { removePost(id: String!): Post! + # Edit a file + editFile(input: EditFileInput!): File! + + # Remove a file + removeFile(id: String!): File + + # Log in login(username: String!, password: String!): Session! diff --git a/schema/types/file.graphql b/schema/types/file.graphql new file mode 100644 index 0000000..8bdb9bc --- /dev/null +++ b/schema/types/file.graphql @@ -0,0 +1,43 @@ +# A File contains information about a file and where to download it. +type File { + # The file's unique ID + id: String! + + # The kind of file. Most will be "upload", but some that are ported from the wiki will have other values for this. + kind: String! + + # The time of uploading + time: String! + + # Whether the file is publicly listable. Someone with knowledge of the ID + # will still be able to view it, however. + public: Boolean! + + # The file's name + name: String! + + # The MIME type of the file + mimeType: String! + + # The file's size in bytes + size: Int! + + # The uploader + author: String! + + # The URL where the file is hosted + url: String +} + +# Input for editFile mutation +input EditFileInput { + # The file's unique ID + id: String! + + # Whether the file is publicly listable. Someone with knowledge of the ID + # will still be able to view it, however. + public: Boolean + + # The file's name + name: String +} \ No newline at end of file