mirror of
https://github.com/kowainik/hit-on.git
synced 2024-10-26 11:47:25 +03:00
* [#15] When new branch for issue created add the user as assignee Resolves #15 * Improve code after review
This commit is contained in:
parent
fdc18f025b
commit
7aaef21059
@ -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)).
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user