Skip to content

Commit

Permalink
feat(sprint-poker): Can vote on GitLab issue (#6398)
Browse files Browse the repository at this point in the history
* render list of gitlab issues

* use usePaginationFragment

* increase default value for usePaginationFragment

* implement GitLabScopingSearchResultItem

* add GitLabScopingSelectAllIssues

* can add and remove gitlab issues in scope phase

* add UpdatePokerScopeMutation gitlab optimistic updater

* able to select all issues

* clean up type errors

* add projects name alias to rootSchema

* include projectIds in search query

* return iid from GitLabIssueId

* sort projects by lastActivityAt

* implement fetchGitLabProjects

* implement gitlab issue menu

* implement NewGitLabIssueMenuRoot instead of using useAllIntegrations

* load next if projects dont have any issues

* update comment

* remove gitlab menu root and use defaultProjects query to populate menu

* increase projectsFirst from 10 to 20

* query all gitlab projects

* chore(comment): how to extend BaseTaskIntegration

* able to create a new gitlab issue

* use GitLabServerManager and implement parseWebPath

* fix undefined baseUri

* changed taskIntegrationGitLab and GitLabId to use providerId

* lowercase gitlabRequest to be consistent with gh

* add handle create gitlab issue

* add info fragment and return data instead of data.issue

* add serverBaseUrl

* adjusted root schema and can now render projects in input menu again

* clean up update poker scope and create task gitlab

* fix ts errors

* add gitlab query types

* add fullPath to gitLab issue edge if exists

* get nodes appearing on insert

* get gitlab issue title in create task updater

* include webUrl in createTask query so user can click on newly create issue url

* add first and sort to allProjects query

* rename GitLabRepo to GitLabProject

* pass meetingId to issue input rather than querying it

* add gitlab search query

* filter by gitlab search query

* refactor to hooks

* query projects from project filter menu

* remove __typename and resolveTypes

* update UpdatePokerScopeMutation to fix selectAll bug

* render projects in filter menu

* use fullPath in gitlab menu and adjust max width

* refactor gitlab search query from string to object with projectIds

* selecting a project in the filter menu adds the item to selectedProjectsFullPath

* selecting a project filters the results

* add search icon

* add search icon

* remove searchQuery from scoping results query

* clean-up return statement in fetchGitLabProjects

* remove alias

* make selectedProjectIds nullable

* add search to issue args

* include search string when adding new gitlab issue

* refactor search query to a gql object

* remove useLoadNextOnScrollBottom and increase default projects first

* merge with master

* fix selectedProjects type err

* add search string to differentiate project menu query and include ids in project connection so we can add issues with a filter

* use react-swipeable-views workaround

* add _xGitLabProject resolver

* merge with master

* remove resolverTypes and gitlabTypes

* map over tabs instead of contents

* implement new scope search ui in gitlab

* refactor gitlabSearchQuery from selectedProjectsIds to selectedProjects

* selected projects showing up in current filters

* truncate current filters

* add new scope search to jira

* show jira project names

* improve current filters positioning

* remove gitlab types

* implement new scope search ui in parabol integration

* implement new scope search bar ui in github

* change filter var to status

* query projects from GitLabIntegration and remove fullPath from gitlab search query

* refactor scoping results query to usePagination and add alias to new issue query so it is not affected by parent query filtering

* refactor PokerEstimateHeaderCard to make PokerEstimateHeaderCardContent reusable

* adding commit to play by the gh title rules

* track start of search

* track end of search

* track updated poker scope

* fix(gitlab): add proper client-side alias handling (#6361)

* resolve to aliased fields

* resolve to aliased fields

* make poker input menu a dropdown and fix width

* refactor baseTabs to include Component

* create a single source of truth for gitlab issue args

* add viewerMeetingMember check and remove selectedProjectsIds resolver

* track selected gitlab project filter

* track selected all issues

* track cleared gitlab search

* remove refetchable from gitlab scoping results query

* fix ts error

* keep focus in search input after clearing query

* change searchQueryFilters event track options from string to id

* use nullish coalescing instead of logical or

* make selectedProjectsIds null if empty array

* update SegmentEventTrackOptions

* render gitlab menu ui

* implement gitlab dimension query and mutation

* use optional chaining rather than destructuring many vars

* use gid instead of project path in gitlab dimension queries

* begin implementing pushEstimateToGitLab

* feat(sprint-poker): GitLab issue is visible in Estimate phase (#6355)

* refactor PokerEstimateHeaderCard to make PokerEstimateHeaderCardContent reusable

* adding commit to play by the gh title rules

* use nullish coalescing instead of logical or

* spread headerFields into PokerEstimateHeaderCardContent

* able to push comment to gitlab issue

* fix linting errors

* change mutation name to createNote

* remove log

* update types in pushEstimateToGitLab

* update pushEstimateToGitLab checks

* use new sdl pattern in updateGitLabDimensionField

* change teamId to varchar(100)

* use projectId and providerId in gitlab dimension queries

* use GetIssueQuery type

* return success payload for updateGitlabDimensionField mutation

* use accessUserId instead of viewerId for the gitlabAuth

Co-authored-by: Matt Krick <[email protected]>
  • Loading branch information
nickoferrall and mattkrick authored May 4, 2022
1 parent 8ebf6df commit 54c309e
Show file tree
Hide file tree
Showing 24 changed files with 724 additions and 4 deletions.
1 change: 1 addition & 0 deletions codegen.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"CreateImposterTokenPayload": "./types/CreateImposterTokenPayload#CreateImposterTokenPayloadSource",
"LoginWithGooglePayload": "./types/LoginWithGooglePayload#LoginWithGooglePayloadSource",
"UpsertTeamPromptResponseSuccess": "./types/UpsertTeamPromptResponseSuccess#UpsertTeamPromptResponseSuccessSource",
"UpdateGitLabDimensionFieldSuccess": "./types/UpdateGitLabDimensionFieldSuccess#UpdateGitLabDimensionFieldSuccessSource",
"Organization": "../../database/types/Organization#default as Organization",
"TeamPromptResponse": "../../postgres/queries/getTeamPromptResponsesByIds#TeamPromptResponse",
"NewMeeting": "../../postgres/types/Meeting#AnyMeeting",
Expand Down
89 changes: 89 additions & 0 deletions packages/client/components/GitLabFieldDimensionDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {useFragment} from 'react-relay'
import {PALETTE} from '~/styles/paletteV3'
import {MenuPosition} from '../hooks/useCoords'
import useMenu from '../hooks/useMenu'
import {ICON_SIZE} from '../styles/typographyV2'
import {SprintPokerDefaults} from '../types/constEnums'
import {GitLabFieldDimensionDropdown_stage$key} from '../__generated__/GitLabFieldDimensionDropdown_stage.graphql'
import GitLabFieldMenu from './GitLabFieldMenu'
import Icon from './Icon'
import PlainButton from './PlainButton/PlainButton'

interface Props {
clearError: () => void
isFacilitator: boolean
stageRef: GitLabFieldDimensionDropdown_stage$key
submitScore(): void
}

const Wrapper = styled(PlainButton)<{isFacilitator: boolean}>(({isFacilitator}) => ({
color: PALETTE.SLATE_700,
cursor: isFacilitator ? undefined : 'default',
display: 'flex',
paddingRight: isFacilitator ? undefined : 8,
userSelect: 'none',
':hover,:focus,:active': {
opacity: isFacilitator ? '50%' : undefined
}
}))

const CurrentValue = styled('div')({
fontSize: 14
})

const StyledIcon = styled(Icon)<{isFacilitator: boolean}>(({isFacilitator}) => ({
fontSize: ICON_SIZE.MD18,
display: isFacilitator ? undefined : 'none'
}))

const labelLookup = {
[SprintPokerDefaults.SERVICE_FIELD_COMMENT]: SprintPokerDefaults.SERVICE_FIELD_COMMENT_LABEL,
[SprintPokerDefaults.SERVICE_FIELD_NULL]: SprintPokerDefaults.SERVICE_FIELD_NULL_LABEL
}

const GitLabFieldDimensionDropdown = (props: Props) => {
const {clearError, stageRef, isFacilitator, submitScore} = props
const stage = useFragment(
graphql`
fragment GitLabFieldDimensionDropdown_stage on EstimateStage {
...GitLabFieldMenu_stage
finalScore
serviceField {
name
}
}
`,
stageRef
)
const {serviceField} = stage
const {name: serviceFieldName} = serviceField
const {togglePortal, menuPortal, originRef, menuProps} = useMenu<HTMLButtonElement>(
MenuPosition.UPPER_RIGHT,
{isDropdown: true}
)

const onClick = () => {
if (!isFacilitator) return
togglePortal()
clearError()
}

const label = labelLookup[serviceFieldName]

return (
<>
<Wrapper isFacilitator={isFacilitator} onClick={onClick} ref={originRef}>
<CurrentValue>{label}</CurrentValue>
<StyledIcon isFacilitator={isFacilitator}>{'expand_more'}</StyledIcon>
</Wrapper>
{menuPortal(
<GitLabFieldMenu menuProps={menuProps} stageRef={stage} submitScore={submitScore} />
)}
</>
)
}

export default GitLabFieldDimensionDropdown
99 changes: 99 additions & 0 deletions packages/client/components/GitLabFieldMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {useFragment} from 'react-relay'
import useAtmosphere from '../hooks/useAtmosphere'
import {MenuProps} from '../hooks/useMenu'
import UpdateGitLabDimensionFieldMutation from '../mutations/UpdateGitLabDimensionFieldMutation'
import {SprintPokerDefaults} from '../types/constEnums'
import {GitLabFieldMenu_stage$key} from '../__generated__/GitLabFieldMenu_stage.graphql'
import Menu from './Menu'
import MenuItem from './MenuItem'

interface Props {
menuProps: MenuProps
stageRef: GitLabFieldMenu_stage$key
submitScore(): void
}

const GitLabFieldMenu = (props: Props) => {
const {menuProps, stageRef, submitScore} = props
const atmosphere = useAtmosphere()
const stage = useFragment(
graphql`
fragment GitLabFieldMenu_stage on EstimateStage {
serviceField {
name
}
dimensionRef {
name
}
task {
integration {
... on _xGitLabIssue {
__typename
id
projectId
}
}
}
meetingId
}
`,
stageRef
)
const {portalStatus, isDropdown, closePortal} = menuProps
const {serviceField, task, dimensionRef, meetingId} = stage
const {name: dimensionName} = dimensionRef
const {name: serviceFieldName} = serviceField
const defaults = [
SprintPokerDefaults.SERVICE_FIELD_COMMENT,
SprintPokerDefaults.SERVICE_FIELD_NULL
] as string[]
const defaultActiveIdx = defaults.indexOf(serviceFieldName)

if (task?.integration?.__typename !== '_xGitLabIssue') return null
const {integration} = task
const {projectId} = integration
const handleClick = (labelTemplate: string) => () => {
if (labelTemplate !== serviceFieldName) {
UpdateGitLabDimensionFieldMutation(
atmosphere,
{
dimensionName,
labelTemplate,
projectId,
meetingId
},
{
onCompleted: submitScore,
onError: () => {
/* noop */
}
}
)
} else {
submitScore()
}
closePortal()
}

return (
<Menu
ariaLabel={'Select whether to publish estimate as a comment in GitLab'}
portalStatus={portalStatus}
isDropdown={isDropdown}
defaultActiveIdx={defaultActiveIdx}
>
<MenuItem
label={SprintPokerDefaults.SERVICE_FIELD_COMMENT_LABEL}
onClick={handleClick(SprintPokerDefaults.SERVICE_FIELD_COMMENT)}
/>
<MenuItem
label={SprintPokerDefaults.SERVICE_FIELD_NULL_LABEL}
onClick={handleClick(SprintPokerDefaults.SERVICE_FIELD_NULL)}
/>
</Menu>
)
}

export default GitLabFieldMenu
12 changes: 11 additions & 1 deletion packages/client/components/PokerDimensionFinalScorePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Breakpoint} from '~/types/constEnums'
import {PALETTE} from '../styles/paletteV3'
import {PokerDimensionFinalScorePicker_stage$key} from '../__generated__/PokerDimensionFinalScorePicker_stage.graphql'
import GitHubFieldDimensionDropdown from './GitHubFieldDimensionDropdown'
import GitLabFieldDimensionDropdown from './GitLabFieldDimensionDropdown'
import JiraFieldDimensionDropdown from './JiraFieldDimensionDropdown'
import LinkButton from './LinkButton'
import StyledError from './StyledError'
Expand Down Expand Up @@ -81,6 +82,7 @@ const PokerDimensionFinalScorePicker = (props: Props) => {
fragment PokerDimensionFinalScorePicker_stage on EstimateStage {
...GitHubFieldDimensionDropdown_stage
...JiraFieldDimensionDropdown_stage
...GitLabFieldDimensionDropdown_stage
task {
integration {
__typename
Expand All @@ -97,6 +99,7 @@ const PokerDimensionFinalScorePicker = (props: Props) => {
_xGitHubIssue: 'GitHub',
JiraIssue: 'Jira',
JiraServerIssue: 'Jira Server',
_xGitLabIssue: 'GitLab',
AzureDevOpsWorkItem: 'Azure DevOps'
}
const title = titleByType[integrationType]
Expand Down Expand Up @@ -124,7 +127,6 @@ const PokerDimensionFinalScorePicker = (props: Props) => {
submitScore={submitScore}
/>
)}

{(integrationType === 'JiraIssue' ||
integrationType === 'JiraServerIssue' ||
integrationType === 'AzureDevOpsWorkItem') && (
Expand All @@ -135,6 +137,14 @@ const PokerDimensionFinalScorePicker = (props: Props) => {
submitScore={submitScore}
/>
)}
{integrationType === '_xGitLabIssue' && (
<GitLabFieldDimensionDropdown
clearError={clearError}
stageRef={stage}
isFacilitator={isFacilitator}
submitScore={submitScore}
/>
)}
</ControlWrapper>
</Mapper>
</Wrapper>
Expand Down
1 change: 0 additions & 1 deletion packages/client/components/PokerDimensionValueControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ const PokerDimensionValueControl = (props: Props) => {
/>
</MiniPokerCard>
{!isFacilitator && <Label>{label}</Label>}

<PokerDimensionFinalScorePicker
canUpdate={isStale}
stageRef={stage}
Expand Down
87 changes: 87 additions & 0 deletions packages/client/mutations/UpdateGitLabDimensionFieldMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import graphql from 'babel-plugin-relay/macro'
import {commitMutation} from 'react-relay'
import {DiscriminateProxy} from '../types/generics'
import {StandardMutation} from '../types/relayMutations'
import createProxyRecord from '../utils/relay/createProxyRecord'
import {GitLabFieldMenu_stage} from '../__generated__/GitLabFieldMenu_stage.graphql'
import {UpdateGitLabDimensionFieldMutation as TUpdateGitLabDimensionFieldMutation} from '../__generated__/UpdateGitLabDimensionFieldMutation.graphql'

graphql`
fragment UpdateGitLabDimensionFieldMutation_team on UpdateGitLabDimensionFieldSuccess {
meeting {
phases {
... on EstimatePhase {
stages {
serviceField {
name
type
}
}
}
}
}
}
`

const mutation = graphql`
mutation UpdateGitLabDimensionFieldMutation(
$dimensionName: String!
$labelTemplate: String!
$projectId: Int!
$meetingId: ID!
) {
updateGitLabDimensionField(
dimensionName: $dimensionName
labelTemplate: $labelTemplate
projectId: $projectId
meetingId: $meetingId
) {
... on ErrorPayload {
error {
message
}
}
...UpdateGitLabDimensionFieldMutation_team @relay(mask: false)
}
}
`

const UpdateGitLabDimensionFieldMutation: StandardMutation<TUpdateGitLabDimensionFieldMutation> = (
atmosphere,
variables,
{onCompleted, onError}
) => {
return commitMutation<TUpdateGitLabDimensionFieldMutation>(atmosphere, {
mutation,
variables,
optimisticUpdater: (store) => {
const {dimensionName, labelTemplate, projectId, meetingId} = variables
const meeting = store.get(meetingId)
if (!meeting) return
const phases = meeting.getLinkedRecords('phases')
if (!phases) return
const estimatePhase = phases.find((phase) => phase.getValue('phaseType') === 'ESTIMATE')!
const stages = estimatePhase.getLinkedRecords<GitLabFieldMenu_stage[]>('stages')

stages.forEach((stage) => {
const dimensionRef = stage.getLinkedRecord('dimensionRef')
const dimensionRefName = dimensionRef.getValue('name')
if (dimensionRefName !== dimensionName) return
const task = stage.getLinkedRecord('task')
const _integration = task.getLinkedRecord('integration')
if (_integration.getType() !== '_xGitLabIssue') return
const integration = _integration as DiscriminateProxy<typeof _integration, '_xGitLabIssue'>
if (integration.getValue('projectId') !== projectId) return
const nextServiceField = createProxyRecord(store, 'ServiceField', {
name: labelTemplate,
type: 'string'
})
stage.setLinkedRecord(nextServiceField, 'serviceField')
})
},
onCompleted,
onError
})
}

export default UpdateGitLabDimensionFieldMutation
26 changes: 26 additions & 0 deletions packages/server/dataloader/customLoaderMakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import getGitHubAuthByUserIdTeamId, {
import getGitHubDimensionFieldMaps, {
GitHubDimensionFieldMap
} from '../postgres/queries/getGitHubDimensionFieldMaps'
import getGitLabDimensionFieldMaps, {
GitLabDimensionFieldMap
} from '../postgres/queries/getGitLabDimensionFieldMaps'
import getLatestTaskEstimates from '../postgres/queries/getLatestTaskEstimates'
import getMeetingTaskEstimates, {
MeetingTaskEstimatesResult
Expand Down Expand Up @@ -240,6 +243,29 @@ export const githubAuth = (parent: RootDataLoader) => {
)
}

export const gitlabDimensionFieldMaps = (parent: RootDataLoader) => {
return new DataLoader<
{teamId: string; dimensionName: string; projectId: number; providerId: number},
GitLabDimensionFieldMap | null,
string
>(
async (keys) => {
const results = await Promise.allSettled(
keys.map(async ({teamId, dimensionName, projectId, providerId}) =>
getGitLabDimensionFieldMaps(teamId, dimensionName, projectId, providerId)
)
)
const vals = results.map((result) => (result.status === 'fulfilled' ? result.value : null))
return vals
},
{
...parent.dataLoaderOptions,
cacheKeyFn: ({teamId, dimensionName, projectId, providerId}) =>
`${teamId}:${dimensionName}:${projectId}:${providerId}`
}
)
}

export const githubDimensionFieldMaps = (parent: RootDataLoader) => {
return new DataLoader<
{teamId: string; dimensionName: string; nameWithOwner: string},
Expand Down
Loading

0 comments on commit 54c309e

Please sign in to comment.