mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-15 02:01:43 +03:00
implement media hosting in git for comments + API for the webui
This commit is contained in:
parent
ed8f7eca9a
commit
d8f89726fe
25
bug/bug.go
25
bug/bug.go
@ -259,7 +259,7 @@ func (bug *Bug) Append(op Operation) {
|
|||||||
// Write the staging area in Git and move the operations to the packs
|
// Write the staging area in Git and move the operations to the packs
|
||||||
func (bug *Bug) Commit(repo repository.Repo) error {
|
func (bug *Bug) Commit(repo repository.Repo) error {
|
||||||
if bug.staging.IsEmpty() {
|
if bug.staging.IsEmpty() {
|
||||||
return fmt.Errorf("can't commit an empty bug")
|
return fmt.Errorf("can't commit a bug with no pending operation")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the Ops as a Git blob containing the serialized array
|
// Write the Ops as a Git blob containing the serialized array
|
||||||
@ -272,14 +272,31 @@ func (bug *Bug) Commit(repo repository.Repo) error {
|
|||||||
bug.rootPack = hash
|
bug.rootPack = hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a Git tree referencing this blob
|
// Make a Git tree referencing this blob and all needed files
|
||||||
hash, err = repo.StoreTree([]repository.TreeEntry{
|
tree := []repository.TreeEntry{
|
||||||
// the last pack of ops
|
// the last pack of ops
|
||||||
{ObjectType: repository.Blob, Hash: hash, Name: opsEntryName},
|
{ObjectType: repository.Blob, Hash: hash, Name: opsEntryName},
|
||||||
// always the first pack of ops (might be the same)
|
// always the first pack of ops (might be the same)
|
||||||
{ObjectType: repository.Blob, Hash: bug.rootPack, Name: rootEntryName},
|
{ObjectType: repository.Blob, Hash: bug.rootPack, Name: rootEntryName},
|
||||||
})
|
}
|
||||||
|
|
||||||
|
counter := 0
|
||||||
|
added := make(map[util.Hash]interface{})
|
||||||
|
for _, ops := range bug.staging.Operations {
|
||||||
|
for _, file := range ops.Files() {
|
||||||
|
if _, has := added[file]; !has {
|
||||||
|
tree = append(tree, repository.TreeEntry{
|
||||||
|
ObjectType: repository.Blob,
|
||||||
|
Hash: file,
|
||||||
|
Name: fmt.Sprintf("file%d", counter),
|
||||||
|
})
|
||||||
|
counter++
|
||||||
|
added[file] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err = repo.StoreTree(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package bug
|
package bug
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -8,6 +9,7 @@ import (
|
|||||||
type Comment struct {
|
type Comment struct {
|
||||||
Author Person
|
Author Person
|
||||||
Message string
|
Message string
|
||||||
|
Files []util.Hash
|
||||||
|
|
||||||
// Creation time of the comment.
|
// Creation time of the comment.
|
||||||
// Should be used only for human display, never for ordering as we can't rely on it in a distributed system.
|
// Should be used only for human display, never for ordering as we can't rely on it in a distributed system.
|
||||||
|
@ -11,7 +11,7 @@ func (l Label) String() string {
|
|||||||
return string(l)
|
return string(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalGQL implements the graphql.Marshaler interface
|
// UnmarshalGQL implements the graphql.Unmarshaler interface
|
||||||
func (l *Label) UnmarshalGQL(v interface{}) error {
|
func (l *Label) UnmarshalGQL(v interface{}) error {
|
||||||
_, ok := v.(string)
|
_, ok := v.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package bug
|
package bug
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type OperationType int
|
type OperationType int
|
||||||
|
|
||||||
@ -17,6 +20,7 @@ type Operation interface {
|
|||||||
OpType() OperationType
|
OpType() OperationType
|
||||||
Time() time.Time
|
Time() time.Time
|
||||||
Apply(snapshot Snapshot) Snapshot
|
Apply(snapshot Snapshot) Snapshot
|
||||||
|
Files() []util.Hash
|
||||||
|
|
||||||
// TODO: data validation (ex: a title is a single line)
|
// TODO: data validation (ex: a title is a single line)
|
||||||
// Validate() bool
|
// Validate() bool
|
||||||
|
@ -2,6 +2,7 @@ package operations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCommentOperation will add a new comment in the bug
|
// AddCommentOperation will add a new comment in the bug
|
||||||
@ -11,12 +12,14 @@ var _ bug.Operation = AddCommentOperation{}
|
|||||||
type AddCommentOperation struct {
|
type AddCommentOperation struct {
|
||||||
bug.OpBase
|
bug.OpBase
|
||||||
Message string
|
Message string
|
||||||
|
files []util.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
||||||
comment := bug.Comment{
|
comment := bug.Comment{
|
||||||
Message: op.Message,
|
Message: op.Message,
|
||||||
Author: op.Author,
|
Author: op.Author,
|
||||||
|
Files: op.files,
|
||||||
UnixTime: op.UnixTime,
|
UnixTime: op.UnixTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,15 +28,24 @@ func (op AddCommentOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
|||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddCommentOp(author bug.Person, message string) AddCommentOperation {
|
func (op AddCommentOperation) Files() []util.Hash {
|
||||||
|
return op.files
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddCommentOp(author bug.Person, message string, files []util.Hash) AddCommentOperation {
|
||||||
return AddCommentOperation{
|
return AddCommentOperation{
|
||||||
OpBase: bug.NewOpBase(bug.AddCommentOp, author),
|
OpBase: bug.NewOpBase(bug.AddCommentOp, author),
|
||||||
Message: message,
|
Message: message,
|
||||||
|
files: files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience function to apply the operation
|
// Convenience function to apply the operation
|
||||||
func Comment(b *bug.Bug, author bug.Person, message string) {
|
func Comment(b *bug.Bug, author bug.Person, message string) {
|
||||||
addCommentOp := NewAddCommentOp(author, message)
|
CommentWithFiles(b, author, message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommentWithFiles(b *bug.Bug, author bug.Person, message string, files []util.Hash) {
|
||||||
|
addCommentOp := NewAddCommentOp(author, message, files)
|
||||||
b.Append(addCommentOp)
|
b.Append(addCommentOp)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package operations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateOperation define the initial creation of a bug
|
// CreateOperation define the initial creation of a bug
|
||||||
@ -12,6 +13,7 @@ type CreateOperation struct {
|
|||||||
bug.OpBase
|
bug.OpBase
|
||||||
Title string
|
Title string
|
||||||
Message string
|
Message string
|
||||||
|
files []util.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
||||||
@ -28,18 +30,27 @@ func (op CreateOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
|||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateOp(author bug.Person, title, message string) CreateOperation {
|
func (op CreateOperation) Files() []util.Hash {
|
||||||
|
return op.files
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateOp(author bug.Person, title, message string, files []util.Hash) CreateOperation {
|
||||||
return CreateOperation{
|
return CreateOperation{
|
||||||
OpBase: bug.NewOpBase(bug.CreateOp, author),
|
OpBase: bug.NewOpBase(bug.CreateOp, author),
|
||||||
Title: title,
|
Title: title,
|
||||||
Message: message,
|
Message: message,
|
||||||
|
files: files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience function to apply the operation
|
// Convenience function to apply the operation
|
||||||
func Create(author bug.Person, title, message string) (*bug.Bug, error) {
|
func Create(author bug.Person, title, message string) (*bug.Bug, error) {
|
||||||
|
return CreateWithFiles(author, title, message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWithFiles(author bug.Person, title, message string, files []util.Hash) (*bug.Bug, error) {
|
||||||
newBug := bug.NewBug()
|
newBug := bug.NewBug()
|
||||||
createOp := NewCreateOp(author, title, message)
|
createOp := NewCreateOp(author, title, message, files)
|
||||||
newBug.Append(createOp)
|
newBug.Append(createOp)
|
||||||
|
|
||||||
return newBug, nil
|
return newBug, nil
|
||||||
|
@ -3,6 +3,7 @@ package operations
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sort"
|
"sort"
|
||||||
@ -50,6 +51,10 @@ AddLoop:
|
|||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (op LabelChangeOperation) Files() []util.Hash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewLabelChangeOperation(author bug.Person, added, removed []bug.Label) LabelChangeOperation {
|
func NewLabelChangeOperation(author bug.Person, added, removed []bug.Label) LabelChangeOperation {
|
||||||
return LabelChangeOperation{
|
return LabelChangeOperation{
|
||||||
OpBase: bug.NewOpBase(bug.LabelChangeOp, author),
|
OpBase: bug.NewOpBase(bug.LabelChangeOp, author),
|
||||||
|
@ -2,6 +2,7 @@ package operations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetStatusOperation will change the status of a bug
|
// SetStatusOperation will change the status of a bug
|
||||||
@ -19,6 +20,10 @@ func (op SetStatusOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
|||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (op SetStatusOperation) Files() []util.Hash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewSetStatusOp(author bug.Person, status bug.Status) SetStatusOperation {
|
func NewSetStatusOp(author bug.Person, status bug.Status) SetStatusOperation {
|
||||||
return SetStatusOperation{
|
return SetStatusOperation{
|
||||||
OpBase: bug.NewOpBase(bug.SetStatusOp, author),
|
OpBase: bug.NewOpBase(bug.SetStatusOp, author),
|
||||||
|
@ -2,6 +2,7 @@ package operations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetTitleOperation will change the title of a bug
|
// SetTitleOperation will change the title of a bug
|
||||||
@ -19,6 +20,10 @@ func (op SetTitleOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
|
|||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (op SetTitleOperation) Files() []util.Hash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewSetTitleOp(author bug.Person, title string) SetTitleOperation {
|
func NewSetTitleOp(author bug.Person, title string) SetTitleOperation {
|
||||||
return SetTitleOperation{
|
return SetTitleOperation{
|
||||||
OpBase: bug.NewOpBase(bug.SetTitleOp, author),
|
OpBase: bug.NewOpBase(bug.SetTitleOp, author),
|
||||||
|
15
cache/cache.go
vendored
15
cache/cache.go
vendored
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
"github.com/MichaelMure/git-bug/bug/operations"
|
"github.com/MichaelMure/git-bug/bug/operations"
|
||||||
"github.com/MichaelMure/git-bug/repository"
|
"github.com/MichaelMure/git-bug/repository"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cacher interface {
|
type Cacher interface {
|
||||||
@ -26,6 +27,7 @@ type RepoCacher interface {
|
|||||||
|
|
||||||
// Mutations
|
// Mutations
|
||||||
NewBug(title string, message string) (BugCacher, error)
|
NewBug(title string, message string) (BugCacher, error)
|
||||||
|
NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BugCacher interface {
|
type BugCacher interface {
|
||||||
@ -34,6 +36,7 @@ type BugCacher interface {
|
|||||||
|
|
||||||
// Mutations
|
// Mutations
|
||||||
AddComment(message string) error
|
AddComment(message string) error
|
||||||
|
AddCommentWithFiles(message string, files []util.Hash) error
|
||||||
ChangeLabels(added []string, removed []string) error
|
ChangeLabels(added []string, removed []string) error
|
||||||
Open() error
|
Open() error
|
||||||
Close() error
|
Close() error
|
||||||
@ -159,12 +162,16 @@ func (c *RepoCache) ClearAllBugs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RepoCache) NewBug(title string, message string) (BugCacher, error) {
|
func (c *RepoCache) NewBug(title string, message string) (BugCacher, error) {
|
||||||
|
return c.NewBugWithFiles(title, message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RepoCache) NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error) {
|
||||||
author, err := bug.GetUser(c.repo)
|
author, err := bug.GetUser(c.repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := operations.Create(author, title, message)
|
b, err := operations.CreateWithFiles(author, title, message, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -208,12 +215,16 @@ func (c *BugCache) ClearSnapshot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *BugCache) AddComment(message string) error {
|
func (c *BugCache) AddComment(message string) error {
|
||||||
|
return c.AddCommentWithFiles(message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BugCache) AddCommentWithFiles(message string, files []util.Hash) error {
|
||||||
author, err := bug.GetUser(c.repo)
|
author, err := bug.GetUser(c.repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
operations.Comment(c.bug, author, message)
|
operations.CommentWithFiles(c.bug, author, message, files)
|
||||||
|
|
||||||
// TODO: perf --> the snapshot could simply be updated with the new op
|
// TODO: perf --> the snapshot could simply be updated with the new op
|
||||||
c.ClearSnapshot()
|
c.ClearSnapshot()
|
||||||
|
@ -64,9 +64,9 @@ func newGitFileHandler(repo repository.Repo) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gfh *gitFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (gfh *gitFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
hash := mux.Vars(r)["hash"]
|
hash := util.Hash(mux.Vars(r)["hash"])
|
||||||
|
|
||||||
if !isGitHash(hash) {
|
if !hash.IsValid() {
|
||||||
http.Error(rw, "invalid git hash", http.StatusBadRequest)
|
http.Error(rw, "invalid git hash", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -144,18 +144,6 @@ func (gufh *gitUploadFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Requ
|
|||||||
rw.Write(js)
|
rw.Write(js)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGitHash(s string) bool {
|
|
||||||
if len(s) != 40 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, r := range s {
|
|
||||||
if (r < 'a' || r > 'z') && (r < '0' || r > '9') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var webUICmd = &cobra.Command{
|
var webUICmd = &cobra.Command{
|
||||||
Use: "webui",
|
Use: "webui",
|
||||||
Short: "Launch the web UI",
|
Short: "Launch the web UI",
|
||||||
|
@ -17,6 +17,8 @@ models:
|
|||||||
model: github.com/MichaelMure/git-bug/bug.Person
|
model: github.com/MichaelMure/git-bug/bug.Person
|
||||||
Label:
|
Label:
|
||||||
model: github.com/MichaelMure/git-bug/bug.Label
|
model: github.com/MichaelMure/git-bug/bug.Label
|
||||||
|
Hash:
|
||||||
|
model: github.com/MichaelMure/git-bug/util.Hash
|
||||||
Operation:
|
Operation:
|
||||||
model: github.com/MichaelMure/git-bug/bug.Operation
|
model: github.com/MichaelMure/git-bug/bug.Operation
|
||||||
CreateOperation:
|
CreateOperation:
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
bug "github.com/MichaelMure/git-bug/bug"
|
bug "github.com/MichaelMure/git-bug/bug"
|
||||||
operations "github.com/MichaelMure/git-bug/bug/operations"
|
operations "github.com/MichaelMure/git-bug/bug/operations"
|
||||||
models "github.com/MichaelMure/git-bug/graphql/models"
|
models "github.com/MichaelMure/git-bug/graphql/models"
|
||||||
|
util "github.com/MichaelMure/git-bug/util"
|
||||||
graphql "github.com/vektah/gqlgen/graphql"
|
graphql "github.com/vektah/gqlgen/graphql"
|
||||||
introspection "github.com/vektah/gqlgen/neelance/introspection"
|
introspection "github.com/vektah/gqlgen/neelance/introspection"
|
||||||
query "github.com/vektah/gqlgen/neelance/query"
|
query "github.com/vektah/gqlgen/neelance/query"
|
||||||
@ -40,8 +41,8 @@ type Resolvers interface {
|
|||||||
|
|
||||||
LabelChangeOperation_date(ctx context.Context, obj *operations.LabelChangeOperation) (time.Time, error)
|
LabelChangeOperation_date(ctx context.Context, obj *operations.LabelChangeOperation) (time.Time, error)
|
||||||
|
|
||||||
Mutation_newBug(ctx context.Context, repoRef *string, title string, message string) (bug.Snapshot, error)
|
Mutation_newBug(ctx context.Context, repoRef *string, title string, message string, files []util.Hash) (bug.Snapshot, error)
|
||||||
Mutation_addComment(ctx context.Context, repoRef *string, prefix string, message string) (bug.Snapshot, error)
|
Mutation_addComment(ctx context.Context, repoRef *string, prefix string, message string, files []util.Hash) (bug.Snapshot, error)
|
||||||
Mutation_changeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error)
|
Mutation_changeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error)
|
||||||
Mutation_open(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
Mutation_open(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
||||||
Mutation_close(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
Mutation_close(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
||||||
@ -87,8 +88,8 @@ type LabelChangeOperationResolver interface {
|
|||||||
Date(ctx context.Context, obj *operations.LabelChangeOperation) (time.Time, error)
|
Date(ctx context.Context, obj *operations.LabelChangeOperation) (time.Time, error)
|
||||||
}
|
}
|
||||||
type MutationResolver interface {
|
type MutationResolver interface {
|
||||||
NewBug(ctx context.Context, repoRef *string, title string, message string) (bug.Snapshot, error)
|
NewBug(ctx context.Context, repoRef *string, title string, message string, files []util.Hash) (bug.Snapshot, error)
|
||||||
AddComment(ctx context.Context, repoRef *string, prefix string, message string) (bug.Snapshot, error)
|
AddComment(ctx context.Context, repoRef *string, prefix string, message string, files []util.Hash) (bug.Snapshot, error)
|
||||||
ChangeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error)
|
ChangeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error)
|
||||||
Open(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
Open(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
||||||
Close(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
Close(ctx context.Context, repoRef *string, prefix string) (bug.Snapshot, error)
|
||||||
@ -139,12 +140,12 @@ func (s shortMapper) LabelChangeOperation_date(ctx context.Context, obj *operati
|
|||||||
return s.r.LabelChangeOperation().Date(ctx, obj)
|
return s.r.LabelChangeOperation().Date(ctx, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s shortMapper) Mutation_newBug(ctx context.Context, repoRef *string, title string, message string) (bug.Snapshot, error) {
|
func (s shortMapper) Mutation_newBug(ctx context.Context, repoRef *string, title string, message string, files []util.Hash) (bug.Snapshot, error) {
|
||||||
return s.r.Mutation().NewBug(ctx, repoRef, title, message)
|
return s.r.Mutation().NewBug(ctx, repoRef, title, message, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s shortMapper) Mutation_addComment(ctx context.Context, repoRef *string, prefix string, message string) (bug.Snapshot, error) {
|
func (s shortMapper) Mutation_addComment(ctx context.Context, repoRef *string, prefix string, message string, files []util.Hash) (bug.Snapshot, error) {
|
||||||
return s.r.Mutation().AddComment(ctx, repoRef, prefix, message)
|
return s.r.Mutation().AddComment(ctx, repoRef, prefix, message, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s shortMapper) Mutation_changeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error) {
|
func (s shortMapper) Mutation_changeLabels(ctx context.Context, repoRef *string, prefix string, added []string, removed []string) (bug.Snapshot, error) {
|
||||||
@ -264,6 +265,8 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel []quer
|
|||||||
out.Values[i] = ec._AddCommentOperation_date(ctx, field, obj)
|
out.Values[i] = ec._AddCommentOperation_date(ctx, field, obj)
|
||||||
case "message":
|
case "message":
|
||||||
out.Values[i] = ec._AddCommentOperation_message(ctx, field, obj)
|
out.Values[i] = ec._AddCommentOperation_message(ctx, field, obj)
|
||||||
|
case "files":
|
||||||
|
out.Values[i] = ec._AddCommentOperation_files(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@ -324,6 +327,26 @@ func (ec *executionContext) _AddCommentOperation_message(ctx context.Context, fi
|
|||||||
return graphql.MarshalString(res)
|
return graphql.MarshalString(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _AddCommentOperation_files(ctx context.Context, field graphql.CollectedField, obj *operations.AddCommentOperation) graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.Object = "AddCommentOperation"
|
||||||
|
rctx.Args = nil
|
||||||
|
rctx.Field = field
|
||||||
|
rctx.PushField(field.Alias)
|
||||||
|
defer rctx.Pop()
|
||||||
|
res := obj.Files()
|
||||||
|
arr1 := graphql.Array{}
|
||||||
|
for idx1 := range res {
|
||||||
|
arr1 = append(arr1, func() graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.PushIndex(idx1)
|
||||||
|
defer rctx.Pop()
|
||||||
|
return res[idx1]
|
||||||
|
}())
|
||||||
|
}
|
||||||
|
return arr1
|
||||||
|
}
|
||||||
|
|
||||||
var bugImplementors = []string{"Bug"}
|
var bugImplementors = []string{"Bug"}
|
||||||
|
|
||||||
// nolint: gocyclo, errcheck, gas, goconst
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
@ -818,6 +841,8 @@ func (ec *executionContext) _Comment(ctx context.Context, sel []query.Selection,
|
|||||||
out.Values[i] = ec._Comment_author(ctx, field, obj)
|
out.Values[i] = ec._Comment_author(ctx, field, obj)
|
||||||
case "message":
|
case "message":
|
||||||
out.Values[i] = ec._Comment_message(ctx, field, obj)
|
out.Values[i] = ec._Comment_message(ctx, field, obj)
|
||||||
|
case "files":
|
||||||
|
out.Values[i] = ec._Comment_files(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@ -848,6 +873,26 @@ func (ec *executionContext) _Comment_message(ctx context.Context, field graphql.
|
|||||||
return graphql.MarshalString(res)
|
return graphql.MarshalString(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Comment_files(ctx context.Context, field graphql.CollectedField, obj *bug.Comment) graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.Object = "Comment"
|
||||||
|
rctx.Args = nil
|
||||||
|
rctx.Field = field
|
||||||
|
rctx.PushField(field.Alias)
|
||||||
|
defer rctx.Pop()
|
||||||
|
res := obj.Files
|
||||||
|
arr1 := graphql.Array{}
|
||||||
|
for idx1 := range res {
|
||||||
|
arr1 = append(arr1, func() graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.PushIndex(idx1)
|
||||||
|
defer rctx.Pop()
|
||||||
|
return res[idx1]
|
||||||
|
}())
|
||||||
|
}
|
||||||
|
return arr1
|
||||||
|
}
|
||||||
|
|
||||||
var commentConnectionImplementors = []string{"CommentConnection"}
|
var commentConnectionImplementors = []string{"CommentConnection"}
|
||||||
|
|
||||||
// nolint: gocyclo, errcheck, gas, goconst
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
@ -1007,6 +1052,8 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel []query.Se
|
|||||||
out.Values[i] = ec._CreateOperation_title(ctx, field, obj)
|
out.Values[i] = ec._CreateOperation_title(ctx, field, obj)
|
||||||
case "message":
|
case "message":
|
||||||
out.Values[i] = ec._CreateOperation_message(ctx, field, obj)
|
out.Values[i] = ec._CreateOperation_message(ctx, field, obj)
|
||||||
|
case "files":
|
||||||
|
out.Values[i] = ec._CreateOperation_files(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@ -1078,6 +1125,26 @@ func (ec *executionContext) _CreateOperation_message(ctx context.Context, field
|
|||||||
return graphql.MarshalString(res)
|
return graphql.MarshalString(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _CreateOperation_files(ctx context.Context, field graphql.CollectedField, obj *operations.CreateOperation) graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.Object = "CreateOperation"
|
||||||
|
rctx.Args = nil
|
||||||
|
rctx.Field = field
|
||||||
|
rctx.PushField(field.Alias)
|
||||||
|
defer rctx.Pop()
|
||||||
|
res := obj.Files()
|
||||||
|
arr1 := graphql.Array{}
|
||||||
|
for idx1 := range res {
|
||||||
|
arr1 = append(arr1, func() graphql.Marshaler {
|
||||||
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
|
rctx.PushIndex(idx1)
|
||||||
|
defer rctx.Pop()
|
||||||
|
return res[idx1]
|
||||||
|
}())
|
||||||
|
}
|
||||||
|
return arr1
|
||||||
|
}
|
||||||
|
|
||||||
var labelChangeOperationImplementors = []string{"LabelChangeOperation", "Operation", "Authored"}
|
var labelChangeOperationImplementors = []string{"LabelChangeOperation", "Operation", "Authored"}
|
||||||
|
|
||||||
// nolint: gocyclo, errcheck, gas, goconst
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
@ -1264,6 +1331,25 @@ func (ec *executionContext) _Mutation_newBug(ctx context.Context, field graphql.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
args["message"] = arg2
|
args["message"] = arg2
|
||||||
|
var arg3 []util.Hash
|
||||||
|
if tmp, ok := field.Args["files"]; ok {
|
||||||
|
var err error
|
||||||
|
var rawIf1 []interface{}
|
||||||
|
if tmp != nil {
|
||||||
|
if tmp1, ok := tmp.([]interface{}); ok {
|
||||||
|
rawIf1 = tmp1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg3 = make([]util.Hash, len(rawIf1))
|
||||||
|
for idx1 := range rawIf1 {
|
||||||
|
err = (&arg3[idx1]).UnmarshalGQL(rawIf1[idx1])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["files"] = arg3
|
||||||
rctx := graphql.GetResolverContext(ctx)
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
rctx.Object = "Mutation"
|
rctx.Object = "Mutation"
|
||||||
rctx.Args = args
|
rctx.Args = args
|
||||||
@ -1271,7 +1357,7 @@ func (ec *executionContext) _Mutation_newBug(ctx context.Context, field graphql.
|
|||||||
rctx.PushField(field.Alias)
|
rctx.PushField(field.Alias)
|
||||||
defer rctx.Pop()
|
defer rctx.Pop()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
|
||||||
return ec.resolvers.Mutation_newBug(ctx, args["repoRef"].(*string), args["title"].(string), args["message"].(string))
|
return ec.resolvers.Mutation_newBug(ctx, args["repoRef"].(*string), args["title"].(string), args["message"].(string), args["files"].([]util.Hash))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -1321,6 +1407,25 @@ func (ec *executionContext) _Mutation_addComment(ctx context.Context, field grap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
args["message"] = arg2
|
args["message"] = arg2
|
||||||
|
var arg3 []util.Hash
|
||||||
|
if tmp, ok := field.Args["files"]; ok {
|
||||||
|
var err error
|
||||||
|
var rawIf1 []interface{}
|
||||||
|
if tmp != nil {
|
||||||
|
if tmp1, ok := tmp.([]interface{}); ok {
|
||||||
|
rawIf1 = tmp1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg3 = make([]util.Hash, len(rawIf1))
|
||||||
|
for idx1 := range rawIf1 {
|
||||||
|
err = (&arg3[idx1]).UnmarshalGQL(rawIf1[idx1])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["files"] = arg3
|
||||||
rctx := graphql.GetResolverContext(ctx)
|
rctx := graphql.GetResolverContext(ctx)
|
||||||
rctx.Object = "Mutation"
|
rctx.Object = "Mutation"
|
||||||
rctx.Args = args
|
rctx.Args = args
|
||||||
@ -1328,7 +1433,7 @@ func (ec *executionContext) _Mutation_addComment(ctx context.Context, field grap
|
|||||||
rctx.PushField(field.Alias)
|
rctx.PushField(field.Alias)
|
||||||
defer rctx.Pop()
|
defer rctx.Pop()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {
|
||||||
return ec.resolvers.Mutation_addComment(ctx, args["repoRef"].(*string), args["prefix"].(string), args["message"].(string))
|
return ec.resolvers.Mutation_addComment(ctx, args["repoRef"].(*string), args["prefix"].(string), args["message"].(string), args["files"].([]util.Hash))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -3108,6 +3213,7 @@ func (ec *executionContext) introspectType(name string) *introspection.Type {
|
|||||||
|
|
||||||
var parsedSchema = schema.MustParse(`scalar Time
|
var parsedSchema = schema.MustParse(`scalar Time
|
||||||
scalar Label
|
scalar Label
|
||||||
|
scalar Hash
|
||||||
|
|
||||||
# Information about pagination in a connection.
|
# Information about pagination in a connection.
|
||||||
type PageInfo {
|
type PageInfo {
|
||||||
@ -3149,6 +3255,9 @@ type Comment implements Authored {
|
|||||||
|
|
||||||
# The message of this comment.
|
# The message of this comment.
|
||||||
message: String!
|
message: String!
|
||||||
|
|
||||||
|
# All media's hash referenced in this comment
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
@ -3188,6 +3297,7 @@ type CreateOperation implements Operation, Authored {
|
|||||||
|
|
||||||
title: String!
|
title: String!
|
||||||
message: String!
|
message: String!
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetTitleOperation implements Operation, Authored {
|
type SetTitleOperation implements Operation, Authored {
|
||||||
@ -3202,6 +3312,7 @@ type AddCommentOperation implements Operation, Authored {
|
|||||||
date: Time!
|
date: Time!
|
||||||
|
|
||||||
message: String!
|
message: String!
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetStatusOperation implements Operation, Authored {
|
type SetStatusOperation implements Operation, Authored {
|
||||||
@ -3291,9 +3402,9 @@ type Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
newBug(repoRef: String, title: String!, message: String!): Bug!
|
newBug(repoRef: String, title: String!, message: String!, files: [Hash!]): Bug!
|
||||||
|
|
||||||
addComment(repoRef: String, prefix: String!, message: String!): Bug!
|
addComment(repoRef: String, prefix: String!, message: String!, files: [Hash!]): Bug!
|
||||||
changeLabels(repoRef: String, prefix: String!, added: [String!], removed: [String!]): Bug!
|
changeLabels(repoRef: String, prefix: String!, added: [String!], removed: [String!]): Bug!
|
||||||
open(repoRef: String, prefix: String!): Bug!
|
open(repoRef: String, prefix: String!): Bug!
|
||||||
close(repoRef: String, prefix: String!): Bug!
|
close(repoRef: String, prefix: String!): Bug!
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/MichaelMure/git-bug/bug"
|
"github.com/MichaelMure/git-bug/bug"
|
||||||
"github.com/MichaelMure/git-bug/cache"
|
"github.com/MichaelMure/git-bug/cache"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mutationResolver struct {
|
type mutationResolver struct {
|
||||||
@ -19,13 +20,13 @@ func (r mutationResolver) getRepo(repoRef *string) (cache.RepoCacher, error) {
|
|||||||
return r.cache.DefaultRepo()
|
return r.cache.DefaultRepo()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) NewBug(ctx context.Context, repoRef *string, title string, message string) (bug.Snapshot, error) {
|
func (r mutationResolver) NewBug(ctx context.Context, repoRef *string, title string, message string, files []util.Hash) (bug.Snapshot, error) {
|
||||||
repo, err := r.getRepo(repoRef)
|
repo, err := r.getRepo(repoRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bug.Snapshot{}, err
|
return bug.Snapshot{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := repo.NewBug(title, message)
|
b, err := repo.NewBugWithFiles(title, message, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bug.Snapshot{}, err
|
return bug.Snapshot{}, err
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ func (r mutationResolver) Commit(ctx context.Context, repoRef *string, prefix st
|
|||||||
return *snap, nil
|
return *snap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r mutationResolver) AddComment(ctx context.Context, repoRef *string, prefix string, message string) (bug.Snapshot, error) {
|
func (r mutationResolver) AddComment(ctx context.Context, repoRef *string, prefix string, message string, files []util.Hash) (bug.Snapshot, error) {
|
||||||
repo, err := r.getRepo(repoRef)
|
repo, err := r.getRepo(repoRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bug.Snapshot{}, err
|
return bug.Snapshot{}, err
|
||||||
@ -67,7 +68,7 @@ func (r mutationResolver) AddComment(ctx context.Context, repoRef *string, prefi
|
|||||||
return bug.Snapshot{}, err
|
return bug.Snapshot{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.AddComment(message)
|
err = b.AddCommentWithFiles(message, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bug.Snapshot{}, err
|
return bug.Snapshot{}, err
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
scalar Time
|
scalar Time
|
||||||
scalar Label
|
scalar Label
|
||||||
|
scalar Hash
|
||||||
|
|
||||||
# Information about pagination in a connection.
|
# Information about pagination in a connection.
|
||||||
type PageInfo {
|
type PageInfo {
|
||||||
@ -41,6 +42,9 @@ type Comment implements Authored {
|
|||||||
|
|
||||||
# The message of this comment.
|
# The message of this comment.
|
||||||
message: String!
|
message: String!
|
||||||
|
|
||||||
|
# All media's hash referenced in this comment
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
@ -80,6 +84,7 @@ type CreateOperation implements Operation, Authored {
|
|||||||
|
|
||||||
title: String!
|
title: String!
|
||||||
message: String!
|
message: String!
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetTitleOperation implements Operation, Authored {
|
type SetTitleOperation implements Operation, Authored {
|
||||||
@ -94,6 +99,7 @@ type AddCommentOperation implements Operation, Authored {
|
|||||||
date: Time!
|
date: Time!
|
||||||
|
|
||||||
message: String!
|
message: String!
|
||||||
|
files: [Hash!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetStatusOperation implements Operation, Authored {
|
type SetStatusOperation implements Operation, Authored {
|
||||||
@ -183,9 +189,9 @@ type Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
newBug(repoRef: String, title: String!, message: String!): Bug!
|
newBug(repoRef: String, title: String!, message: String!, files: [Hash!]): Bug!
|
||||||
|
|
||||||
addComment(repoRef: String, prefix: String!, message: String!): Bug!
|
addComment(repoRef: String, prefix: String!, message: String!, files: [Hash!]): Bug!
|
||||||
changeLabels(repoRef: String, prefix: String!, added: [String!], removed: [String!]): Bug!
|
changeLabels(repoRef: String, prefix: String!, added: [String!], removed: [String!]): Bug!
|
||||||
open(repoRef: String, prefix: String!): Bug!
|
open(repoRef: String, prefix: String!): Bug!
|
||||||
close(repoRef: String, prefix: String!): Bug!
|
close(repoRef: String, prefix: String!): Bug!
|
||||||
|
@ -64,7 +64,7 @@ func (sb *showBug) layout(g *gocui.Gui) error {
|
|||||||
v.Frame = false
|
v.Frame = false
|
||||||
v.BgColor = gocui.ColorBlue
|
v.BgColor = gocui.ColorBlue
|
||||||
|
|
||||||
fmt.Fprintf(v, "[q] Return [c] Add comment [t] Change title")
|
fmt.Fprintf(v, "[q] Return [c] Comment [t] Change title")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = g.SetCurrentView(showBugView)
|
_, err = g.SetCurrentView(showBugView)
|
||||||
|
40
util/hash.go
40
util/hash.go
@ -1,3 +1,43 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
type Hash string
|
type Hash string
|
||||||
|
|
||||||
|
func (h Hash) String() string {
|
||||||
|
return string(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hash) UnmarshalGQL(v interface{}) error {
|
||||||
|
_, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("labels must be strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
*h = v.(Hash)
|
||||||
|
|
||||||
|
if !h.IsValid() {
|
||||||
|
return fmt.Errorf("invalid hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hash) MarshalGQL(w io.Writer) {
|
||||||
|
w.Write([]byte(`"` + h.String() + `"`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hash) IsValid() bool {
|
||||||
|
if len(*h) != 40 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, r := range *h {
|
||||||
|
if (r < 'a' || r > 'z') && (r < '0' || r > '9') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user