[#15] When new branch for issue created add the user as assignee (#141)

* [#15] When new branch for issue created add the user as assignee

Resolves #15

* Improve code after review
This commit is contained in:
Veronika Romashkina 2020-02-10 08:53:54 +00:00 committed by GitHub
parent fdc18f025b
commit 7aaef21059
3 changed files with 88 additions and 25 deletions

View File

@ -11,6 +11,10 @@ The changelog is available [on GitHub][2].
* [#127](https://github.com/kowainik/hit-on/issues/127):
Assign user to issue on `hit new --issue` command.
(by [@chshersh](https://github.com/chshersh)).
* [#15](https://github.com/kowainik/hit-on/issues/15):
When the new branch for the issue is created, add the user as an assignee to
this issue.
(by [@vrom911](https://github.com/vrom911)).
* [#125](https://github.com/kowainik/hit-on/pull/125):
Move to the newer `relude-0.6.0.0`.
(by [@vrom911](https://github.com/vrom911)).

View File

@ -6,11 +6,11 @@ module Hit.Git.New
import Data.Char (isAlphaNum, isDigit, isSpace)
import GitHub (Issue (issueNumber), IssueNumber (..), unIssueNumber)
import GitHub (Issue (issueNumber, issueTitle), IssueNumber (..), unIssueNumber)
import Hit.ColorTerminal (errorMessage, infoMessage, successMessage)
import Hit.Issue (createIssue, getIssueTitle, mkIssueId)
import Hit.Git.Common (getUsername)
import Hit.Issue (assignIssue, createIssue, fetchIssue, mkIssueId)
import qualified Data.Text as T
@ -21,7 +21,7 @@ runNew isIssue issueOrName = do
login <- getUsername
maybeIssue <- if isIssue then tryCreateNewIssue login else pure Nothing
let branchDescription = mkBranchDescription maybeIssue issueOrName
title <- displayBranchDescription branchDescription
title <- assignAndDisplayBranchDescription login branchDescription
let branchName = login <> "/" <> title
"git" ["checkout", "-b", branchName]
where
@ -59,19 +59,23 @@ mkBranchDescription Nothing issueOrName = case readMaybe @Int $ toString issueOr
Just issueNum -> FromIssueNumber issueNum
Nothing -> FromText issueOrName
{- | Display 'BranchDescription' in format:
{- | Assigns the user to the issue if applicable (it current design, if the issue
already exists and user creates the branch for it: 'FromIssueNumber').
Displays 'BranchDescription' in format:
@
123-short-issue-title
@
-}
displayBranchDescription :: BranchDescription -> IO Text
displayBranchDescription = \case
assignAndDisplayBranchDescription :: Text -> BranchDescription -> IO Text
assignAndDisplayBranchDescription username = \case
FromText text -> pure $ mkShortDesc text
FromNewIssue issueNum issueTitle -> pure $ nameWithNumber issueNum issueTitle
FromIssueNumber issueNum -> do
issueTitle <- getIssueTitle $ mkIssueId issueNum
pure $ nameWithNumber issueNum issueTitle
issue <- fetchIssue $ mkIssueId issueNum
assignIssue issue username
pure $ nameWithNumber issueNum $ issueTitle issue
where
nameWithNumber :: Int -> Text -> Text
nameWithNumber issueNum issueTitle =

View File

@ -5,6 +5,8 @@ module Hit.Issue
( -- * For CLI commands
runIssue
, createIssue
, assignIssue
, fetchIssue
-- * Internal helpers
, mkIssueId
@ -19,12 +21,12 @@ import GitHub (Error (..), Id, Issue (..), IssueLabel (..), IssueState (..), Nam
SimpleUser (..), User, getUrl, mkId, mkName, unIssueNumber, untagName)
import GitHub.Auth (Auth (OAuth))
import GitHub.Data.Options (stateOpen)
import GitHub.Endpoints.Issues (NewIssue (..), issue', issuesForRepo')
import GitHub.Endpoints.Issues (EditIssue (..), NewIssue (..), editOfIssue, issue', issuesForRepo')
import Shellmet (($|))
import System.Environment (lookupEnv)
import Hit.ColorTerminal (arrow, blueBg, blueCode, boldCode, errorMessage, greenCode, redCode,
resetCode)
resetCode, successMessage)
import qualified Hit.Formatting as Fmt
import qualified Data.Text as T
@ -48,29 +50,29 @@ getAllIssues :: Maybe Text -> IO ()
getAllIssues me = withOwnerRepo (\t o r -> issuesForRepo' t o r stateOpen) >>= \case
Left err -> errorMessage $ show err
Right is -> do
let maxLen = Fmt.maxLenOn (show . issueNumber) is
let maxLen = Fmt.maxLenOn showIssueNumber is
for_ (my is) $ \i -> do
let thisLen = T.length $ show (issueNumber i)
let thisLen = T.length $ showIssueNumber i
padSize = maxLen - thisLen
putTextLn $ showIssueName blueCode padSize i
where
my :: Vector Issue -> Vector Issue
my issues = case me of
Just (makeName -> username) -> V.filter (assignedTo username . issueAssignees) issues
Just (makeName -> username) -> V.filter (username `isAssignedToIssue`) issues
Nothing -> issues
-- Is the username an element of assignees vector?
assignedTo :: Name User -> Vector SimpleUser -> Bool
assignedTo user = isJust . V.find ((user ==) . simpleUserLogin)
-- | Show issue number with alignment and its name.
showIssueName :: Text -> Int -> Issue -> Text
showIssueName colorCode padSize Issue{..} =
arrow <> colorCode <> " [#" <> show (unIssueNumber issueNumber) <> "] " <> padding <> resetCode <> issueTitle
showIssueName colorCode padSize i@Issue{..} =
arrow <> colorCode <> " [#" <> showIssueNumber i <> "] " <> padding <> resetCode <> issueTitle
where
padding :: Text
padding = T.replicate padSize " "
-- | Show the issue number.
showIssueNumber :: Issue -> Text
showIssueNumber = show . unIssueNumber . issueNumber
-- | Get the 'Issue' by given issue number and pretty print it fully to terminal.
getIssue :: Id Issue -> IO ()
getIssue num = fetchIssue num >>= putTextLn . showIssueFull
@ -114,12 +116,51 @@ showIssueFull i@Issue{..} = T.intercalate "\n" $
-- | Create an 'Issue' by given title 'Text'
-- QUESTION: should we create 'Login' newtype to add more type-safety here?
createIssue :: Text -> Text -> IO (Either Error Issue)
createIssue title login = withOwnerRepo $ \token owner repo -> case token of
Just oAuth -> GitHub.createIssue oAuth owner repo $ mkNewIssue title login
Nothing -> do
let errorText = "Can not get GITHUB_TOKEN"
errorMessage errorText
pure $ Left $ ParseError errorText
createIssue title login = withAuthOwnerRepo $ \token owner repo ->
GitHub.createIssue token owner repo $ mkNewIssue title login
{- | Assign the user to the given 'Issue'.
This function can fail assignment due to the following reasons:
* Auth token fetch failure
* Assignment query to GutHub failure
The function should inform user about corresponding 'Error' in each case and
continue working.
-}
assignIssue :: Issue -> Text -> IO ()
assignIssue issue username = do
res <- withAuthOwnerRepo $ \token owner repo -> do
let assignee :: Name User
assignee = makeName @User username
let curAssignees :: V.Vector (Name User)
curAssignees = V.map simpleUserLogin $ issueAssignees issue
if assignee `isAssignedToIssue` issue
then pure $ Right (issue, True)
else do
-- TODO: this is hack to cheat on GitHub library, as it
-- doesn't use the correct id in query.
let issId = mkIssueId (unIssueNumber $ issueNumber issue)
(, False) <<$>> GitHub.editIssue token owner repo issId editOfIssue
{ editIssueAssignees = Just $ V.cons assignee curAssignees
}
case res of
Right (iss, isAlreadyAssigned) ->
if isAlreadyAssigned
then pass
else successMessage $ "You were assigned to the issue #" <>
showIssueNumber iss
Left err -> do
errorMessage "Can not assign you to the issue."
putTextLn $ " " <> show err
-- | Is the user assigned to the given 'Issue'?
isAssignedToIssue :: Name User -> Issue -> Bool
isAssignedToIssue assignee = V.elem assignee .
V.map simpleUserLogin . issueAssignees
----------------------------------------------------------------------------
-- Helper functions
@ -150,6 +191,20 @@ withOwnerRepo action = getOwnerRepo >>= \case
errorMessage errorText
pure $ Left $ ParseError errorText
{- | Similar to 'withOwnerRepo', but returns the 'UserError' when cannot get the
GitHub Token, as the given action should work with the 'Auth' instead of 'Maybe
Auth'.
-}
withAuthOwnerRepo
:: (Auth -> Name Owner -> Name Repo -> IO (Either Error a))
-> IO (Either Error a)
withAuthOwnerRepo action = withOwnerRepo $ \token owner repo -> case token of
Just auth -> action auth owner repo
Nothing -> do
let errorText = "Can not get GITHUB_TOKEN"
errorMessage errorText
pure $ Left $ UserError errorText
-- | Smart constructor for @'Id' 'Issue'@.
mkIssueId :: Int -> Id Issue
mkIssueId = mkId $ Proxy @Issue