Skip to content

Commit

Permalink
Add support for read-only mode for web UI.
Browse files Browse the repository at this point in the history
Fixes #402.
  • Loading branch information
lukegb committed Jun 18, 2020
1 parent 7aed987 commit 2576866
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 38 deletions.
11 changes: 7 additions & 4 deletions commands/webui.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ import (
"github.com/spf13/cobra"

"github.com/MichaelMure/git-bug/graphql"
"github.com/MichaelMure/git-bug/graphql/config"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/webui"
)

var (
webUIPort int
webUIOpen bool
webUINoOpen bool
webUIPort int
webUIOpen bool
webUINoOpen bool
webUIReadOnly bool
)

const webUIOpenConfigKey = "git-bug.webui.open"
Expand All @@ -46,7 +48,7 @@ func runWebUI(cmd *cobra.Command, args []string) error {

router := mux.NewRouter()

graphqlHandler, err := graphql.NewHandler(repo)
graphqlHandler, err := graphql.NewHandler(repo, config.Config{ReadOnly: webUIReadOnly})
if err != nil {
return err
}
Expand Down Expand Up @@ -261,5 +263,6 @@ func init() {
webUICmd.Flags().BoolVar(&webUIOpen, "open", false, "Automatically open the web UI in the default browser")
webUICmd.Flags().BoolVar(&webUINoOpen, "no-open", false, "Prevent the automatic opening of the web UI in the default browser")
webUICmd.Flags().IntVarP(&webUIPort, "port", "p", 0, "Port to listen to (default is random)")
webUICmd.Flags().BoolVar(&webUIReadOnly, "read-only", false, "Whether to run the web UI in read-only mode")

}
4 changes: 4 additions & 0 deletions doc/man/git-bug-webui.1
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Available git config:
\fB\-p\fP, \fB\-\-port\fP=0
Port to listen to (default is random)

.PP
\fB\-\-read\-only\fP[=false]
Whether to run the web UI in read\-only mode

.PP
\fB\-h\fP, \fB\-\-help\fP[=false]
help for webui
Expand Down
9 changes: 5 additions & 4 deletions doc/md/git-bug_webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ git-bug webui [flags]
### Options

```
--open Automatically open the web UI in the default browser
--no-open Prevent the automatic opening of the web UI in the default browser
-p, --port int Port to listen to (default is random)
-h, --help help for webui
--open Automatically open the web UI in the default browser
--no-open Prevent the automatic opening of the web UI in the default browser
-p, --port int Port to listen to (default is random)
--read-only Whether to run the web UI in read-only mode
-h, --help help for webui
```

### SEE ALSO
Expand Down
7 changes: 7 additions & 0 deletions graphql/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Package config contains configuration for GraphQL stuff.
package config

// Config holds configuration elements.
type Config struct {
ReadOnly bool
}
3 changes: 2 additions & 1 deletion graphql/graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/99designs/gqlgen/client"

"github.com/MichaelMure/git-bug/graphql/config"
"github.com/MichaelMure/git-bug/graphql/models"
"github.com/MichaelMure/git-bug/misc/random_bugs"
"github.com/MichaelMure/git-bug/repository"
Expand All @@ -16,7 +17,7 @@ func TestQueries(t *testing.T) {

random_bugs.FillRepoWithSeed(repo, 10, 42)

handler, err := NewHandler(repo)
handler, err := NewHandler(repo, config.Config{})
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 3 additions & 2 deletions graphql/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/99designs/gqlgen/graphql/handler"

"github.com/MichaelMure/git-bug/graphql/config"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/resolvers"
"github.com/MichaelMure/git-bug/repository"
Expand All @@ -19,9 +20,9 @@ type Handler struct {
*resolvers.RootResolver
}

func NewHandler(repo repository.ClockedRepo) (Handler, error) {
func NewHandler(repo repository.ClockedRepo, cfg config.Config) (Handler, error) {
h := Handler{
RootResolver: resolvers.NewRootResolver(),
RootResolver: resolvers.NewRootResolver(cfg),
}

err := h.RootResolver.RegisterDefaultRepository(repo)
Expand Down
24 changes: 24 additions & 0 deletions graphql/resolvers/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,32 @@ import (
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
"github.com/vektah/gqlparser/gqlerror"
)

var _ graph.MutationResolver = &readonlyMutationResolver{}

type readonlyMutationResolver struct{}

func (readonlyMutationResolver) NewBug(_ context.Context, _ models.NewBugInput) (*models.NewBugPayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}
func (readonlyMutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}
func (readonlyMutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}
func (readonlyMutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}
func (readonlyMutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}
func (readonlyMutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) {
return nil, gqlerror.Errorf("readonly mode")
}

var _ graph.MutationResolver = &mutationResolver{}

type mutationResolver struct {
Expand Down
8 changes: 6 additions & 2 deletions graphql/resolvers/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/graphql/config"
"github.com/MichaelMure/git-bug/graphql/connections"
"github.com/MichaelMure/git-bug/graphql/graph"
"github.com/MichaelMure/git-bug/graphql/models"
Expand All @@ -13,7 +14,7 @@ import (

var _ graph.RepositoryResolver = &repoResolver{}

type repoResolver struct{}
type repoResolver struct{ cfg config.Config }

func (repoResolver) Name(_ context.Context, obj *models.Repository) (*string, error) {
name := obj.Repo.Name()
Expand Down Expand Up @@ -149,7 +150,10 @@ func (repoResolver) Identity(_ context.Context, obj *models.Repository, prefix s
return models.NewLazyIdentity(obj.Repo, excerpt), nil
}

func (repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (models.IdentityWrapper, error) {
func (r repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (models.IdentityWrapper, error) {
if r.cfg.ReadOnly {
return nil, nil
}
excerpt, err := obj.Repo.GetUserIdentityExcerpt()
if err != nil {
return nil, err
Expand Down
14 changes: 10 additions & 4 deletions graphql/resolvers/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ package resolvers

import (
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/graphql/config"
"github.com/MichaelMure/git-bug/graphql/graph"
)

var _ graph.ResolverRoot = &RootResolver{}

type RootResolver struct {
cache.MultiRepoCache
cfg config.Config
}

func NewRootResolver() *RootResolver {
func NewRootResolver(cfg config.Config) *RootResolver {
return &RootResolver{
MultiRepoCache: cache.NewMultiRepoCache(),
cfg: cfg,
}
}

Expand All @@ -25,13 +28,16 @@ func (r RootResolver) Query() graph.QueryResolver {
}

func (r RootResolver) Mutation() graph.MutationResolver {
if r.cfg.ReadOnly {
return &readonlyMutationResolver{}
}
return &mutationResolver{
cache: &r.MultiRepoCache,
}
}

func (RootResolver) Repository() graph.RepositoryResolver {
return &repoResolver{}
func (r RootResolver) Repository() graph.RepositoryResolver {
return &repoResolver{r.cfg}
}

func (RootResolver) Bug() graph.BugResolver {
Expand All @@ -50,7 +56,7 @@ func (RootResolver) Label() graph.LabelResolver {
return &labelResolver{}
}

func (r RootResolver) Identity() graph.IdentityResolver {
func (RootResolver) Identity() graph.IdentityResolver {
return &identityResolver{}
}

Expand Down
2 changes: 2 additions & 0 deletions misc/bash_completion/git-bug
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,8 @@ _git-bug_webui()
two_word_flags+=("--port")
two_word_flags+=("-p")
local_nonpersistent_flags+=("--port=")
flags+=("--read-only")
local_nonpersistent_flags+=("--read-only")

must_have_one_flag=()
must_have_one_noun=()
Expand Down
3 changes: 2 additions & 1 deletion misc/powershell_completion/git-bug
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,10 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
[CompletionResult]::new('--no-open', 'no-open', [CompletionResultType]::ParameterName, 'Prevent the automatic opening of the web UI in the default browser')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Port to listen to (default is random)')
[CompletionResult]::new('--port', 'port', [CompletionResultType]::ParameterName, 'Port to listen to (default is random)')
[CompletionResult]::new('--read-only', 'read-only', [CompletionResultType]::ParameterName, 'Whether to run the web UI in read-only mode')
break
}
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}
}
3 changes: 2 additions & 1 deletion misc/zsh_completion/git-bug
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ function _git-bug_webui {
_arguments \
'--open[Automatically open the web UI in the default browser]' \
'--no-open[Prevent the automatic opening of the web UI in the default browser]' \
'(-p --port)'{-p,--port}'[Port to listen to (default is random)]:'
'(-p --port)'{-p,--port}'[Port to listen to (default is random)]:' \
'--read-only[Whether to run the web UI in read-only mode]'
}

34 changes: 20 additions & 14 deletions webui/src/layout/CurrentIdentity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import Avatar from '@material-ui/core/Avatar';
import { makeStyles } from '@material-ui/core/styles';

import { useCurrentIdentityQuery } from './CurrentIdentity.generated';
import CurrentIdentityContext from './CurrentIdentityContext';

const useStyles = makeStyles(theme => ({
displayName: {
Expand All @@ -13,19 +13,25 @@ const useStyles = makeStyles(theme => ({

const CurrentIdentity = () => {
const classes = useStyles();
const { loading, error, data } = useCurrentIdentityQuery();

if (error || loading || !data?.repository?.userIdentity) return null;

const user = data.repository.userIdentity;
return (
<>
<Avatar src={user.avatarUrl ? user.avatarUrl : undefined}>
{user.displayName.charAt(0).toUpperCase()}
</Avatar>
<div className={classes.displayName}>{user.displayName}</div>
</>
);

return (<CurrentIdentityContext.Consumer>
{context => {
if (!context) return null;
const { loading, error, data } = context as any;

if (error || loading || !data?.repository?.userIdentity) return null;

const user = data.repository.userIdentity;
return (
<>
<Avatar src={user.avatarUrl ? user.avatarUrl : undefined}>
{user.displayName.charAt(0).toUpperCase()}
</Avatar>
<div className={classes.displayName}>{user.displayName}</div>
</>
);
}}
</CurrentIdentityContext.Consumer>);
};

export default CurrentIdentity;
6 changes: 6 additions & 0 deletions webui/src/layout/CurrentIdentityContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';

import { CurrentIdentityQueryResult } from './CurrentIdentity.generated';

const Context = React.createContext(null as CurrentIdentityQueryResult | null);
export default Context;
23 changes: 23 additions & 0 deletions webui/src/layout/ReadonlyHidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

import CurrentIdentityContext from './CurrentIdentityContext';

type Props = { children: React.ReactNode };
const ReadonlyHidden = ({ children }: Props) => (
<CurrentIdentityContext.Consumer>
{context => {
if (!context) return null;
const { loading, error, data } = context;

if (error || loading || !data?.repository?.userIdentity) return null;

return (
<>
{children}
</>
);
}}
</CurrentIdentityContext.Consumer>
);

export default ReadonlyHidden;
7 changes: 5 additions & 2 deletions webui/src/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import React from 'react';

import CssBaseline from '@material-ui/core/CssBaseline';

import CurrentIdentityContext from './CurrentIdentityContext';
import Header from './Header';

import { useCurrentIdentityQuery } from './CurrentIdentity.generated';

type Props = { children: React.ReactNode };
function Layout({ children }: Props) {
return (
<>
<CurrentIdentityContext.Provider value={useCurrentIdentityQuery()}>
<CssBaseline />
<Header />
{children}
</>
</CurrentIdentityContext.Provider>
);
}

Expand Down
10 changes: 7 additions & 3 deletions webui/src/pages/bug/Bug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react';
import Typography from '@material-ui/core/Typography/Typography';
import { makeStyles } from '@material-ui/core/styles';

import ReadonlyHidden from 'src/layout/ReadonlyHidden';

import Author from 'src/components/Author';
import Date from 'src/components/Date';
import Label from 'src/components/Label';
Expand Down Expand Up @@ -88,9 +90,11 @@ function Bug({ bug }: Props) {
<div className={classes.container}>
<div className={classes.timeline}>
<TimelineQuery id={bug.id} />
<div className={classes.commentForm}>
<CommentForm bugId={bug.id} />
</div>
<ReadonlyHidden>
<div className={classes.commentForm}>
<CommentForm bugId={bug.id} />
</div>
</ReadonlyHidden>
</div>
<div className={classes.sidebar}>
<span className={classes.sidebarTitle}>Labels</span>
Expand Down

0 comments on commit 2576866

Please sign in to comment.