From 68db4861330a515f1107b15fc5efd83ff4c0e8cd Mon Sep 17 00:00:00 2001 From: Ritik Kapoor Date: Wed, 6 Nov 2024 10:20:51 +0000 Subject: [PATCH] fix: [CODE-1577] include pr author in browser param in PR listing page (#2868) * fix: [CODE-1577] remove redundant code * refactor: [code-1577] address comments * fix: [code-1577] lint * fix: [CODE-1577] address comments * fix: [code-1577] lint * fix: [code-1577] lint * fix: [code-1577] lint * fix: [code-1577] lint * feat: [code-1577] add author in URL --- app/api/openapi/principals.go | 16 +++++++ web/src/pages/PullRequests/PullRequests.tsx | 38 +++++++++++++-- .../PullRequestsContentHeader.tsx | 8 +++- web/src/services/code/index.tsx | 30 ++++++++++++ web/src/services/code/swagger.yaml | 48 +++++++++++++++++++ web/src/utils/Utils.ts | 1 + 6 files changed, 136 insertions(+), 5 deletions(-) diff --git a/app/api/openapi/principals.go b/app/api/openapi/principals.go index 2ea146f51..c953d5504 100644 --- a/app/api/openapi/principals.go +++ b/app/api/openapi/principals.go @@ -26,6 +26,10 @@ import ( "github.com/swaggest/openapi-go/openapi3" ) +type principalInfoRequest struct { + ID int64 `path:"id"` +} + var QueryParameterQueryPrincipals = openapi3.ParameterOrRef{ Parameter: &openapi3.Parameter{ Name: request.QueryParamQuery, @@ -74,4 +78,16 @@ func buildPrincipals(reflector *openapi3.Reflector) { _ = reflector.SetJSONResponse(&opList, new(usererror.Error), http.StatusInternalServerError) _ = reflector.SetJSONResponse(&opList, new(usererror.Error), http.StatusNotFound) _ = reflector.Spec.AddOperation(http.MethodGet, "/principals", opList) + + getPrincipal := openapi3.Operation{} + getPrincipal.WithTags("principals") + getPrincipal.WithMapOfAnything(map[string]interface{}{"operationId": "getPrincipal"}) + _ = reflector.SetRequest(&getPrincipal, new(principalInfoRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&getPrincipal, new(types.PrincipalInfo), http.StatusOK) + _ = reflector.SetJSONResponse(&getPrincipal, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&getPrincipal, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&getPrincipal, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&getPrincipal, new(usererror.Error), http.StatusForbidden) + _ = reflector.SetJSONResponse(&getPrincipal, new(usererror.Error), http.StatusNotFound) + _ = reflector.Spec.AddOperation(http.MethodGet, "/principals/{id}", getPrincipal) } diff --git a/web/src/pages/PullRequests/PullRequests.tsx b/web/src/pages/PullRequests/PullRequests.tsx index b56522fd0..5701a98d3 100644 --- a/web/src/pages/PullRequests/PullRequests.tsx +++ b/web/src/pages/PullRequests/PullRequests.tsx @@ -52,7 +52,12 @@ import { usePageIndex } from 'hooks/usePageIndex' import { useGetSpaceParam } from 'hooks/useGetSpaceParam' import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams' import { useQueryParams } from 'hooks/useQueryParams' -import type { TypesPullReq, RepoRepositoryOutput, TypesLabelPullReqAssignmentInfo } from 'services/code' +import type { + TypesPullReq, + RepoRepositoryOutput, + TypesLabelPullReqAssignmentInfo, + TypesPrincipalInfo +} from 'services/code' import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination' import { NoResultCard } from 'components/NoResultCard/NoResultCard' import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator' @@ -62,6 +67,7 @@ import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner' import useSpaceSSE from 'hooks/useSpaceSSE' import { TimePopoverWithLocal } from 'utils/timePopoverLocal/TimePopoverWithLocal' import { Label } from 'components/Label/Label' +import { getConfig } from 'services/config' import { PullRequestsContentHeader } from './PullRequestsContentHeader/PullRequestsContentHeader' import css from './PullRequests.module.scss' @@ -70,20 +76,32 @@ const SSE_EVENTS = ['pullreq_updated'] export default function PullRequests() { const { getString } = useStrings() const history = useHistory() - const { routes, hooks, standalone } = useAppContext() + const { routes, hooks, standalone, routingId } = useAppContext() const { CODE_PULLREQ_LABELS: isLabelEnabled } = hooks?.useFeatureFlags() const [searchTerm, setSearchTerm] = useState() const browserParams = useQueryParams() const [filter, setFilter] = useState(browserParams?.state || (PullRequestFilterOption.OPEN as string)) - const [authorFilter, setAuthorFilter] = useState() + const [authorFilter, setAuthorFilter] = useState(browserParams?.author ?? '') const [labelFilter, setLabelFilter] = useState([]) const space = useGetSpaceParam() const { updateQueryParams, replaceQueryParams } = useUpdateQueryParams() const pageInit = browserParams.page ? parseInt(browserParams.page) : 1 const [page, setPage] = usePageIndex(pageInit) + + const { data: principal, refetch: refetchPrincipal } = useGet({ + base: getConfig('code/api/v1'), + path: `/principals/${browserParams.author}`, + queryParams: { + accountIdentifier: routingId + }, + lazy: true, + debounce: 500 + }) + useEffect(() => { const params = { ...browserParams, + ...(Boolean(authorFilter) && { author: authorFilter }), ...(page > 1 && { page: page.toString() }), ...(filter && { state: filter }) } @@ -94,7 +112,18 @@ export default function PullRequests() { delete updateParams.page replaceQueryParams(updateParams, undefined, true) } - }, [page, filter]) // eslint-disable-line react-hooks/exhaustive-deps + + if (!authorFilter && browserParams.author) { + const paramList = { ...params } + delete paramList.author + replaceQueryParams(paramList, undefined, true) + } + + if (browserParams.author) { + refetchPrincipal() + } + }, [page, filter, authorFilter]) // eslint-disable-line react-hooks/exhaustive-deps + const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata() const { data, @@ -385,6 +414,7 @@ export default function PullRequests() { setSearchTerm(value) setPage(1) }} + activePullRequestAuthorObj={principal} activePullRequestAuthorFilterOption={authorFilter} activePullRequestLabelFilterOption={labelFilter} onPullRequestAuthorFilterChanged={_authorFilter => { diff --git a/web/src/pages/PullRequests/PullRequestsContentHeader/PullRequestsContentHeader.tsx b/web/src/pages/PullRequests/PullRequestsContentHeader/PullRequestsContentHeader.tsx index a7bf61e5e..6868f059a 100644 --- a/web/src/pages/PullRequests/PullRequestsContentHeader/PullRequestsContentHeader.tsx +++ b/web/src/pages/PullRequests/PullRequestsContentHeader/PullRequestsContentHeader.tsx @@ -45,6 +45,7 @@ interface PullRequestsContentHeaderProps extends Pick> onPullRequestAuthorFilterChanged: (authorFilter: string) => void @@ -60,6 +61,7 @@ export function PullRequestsContentHeader({ onSearchTermChanged, activePullRequestFilterOption = PullRequestFilterOption.OPEN, activePullRequestAuthorFilterOption, + activePullRequestAuthorObj, activePullRequestLabelFilterOption, repoMetadata }: PullRequestsContentHeaderProps) { @@ -158,7 +160,11 @@ export function PullRequestsContentHeader({ } } ) - const authorsList = await moveCurrentUserToTop(fetchedAuthors, currentUser, query) + + const authors = [...fetchedAuthors, ...(activePullRequestAuthorObj ? [activePullRequestAuthorObj] : [])] + + const authorsList = await moveCurrentUserToTop(authors, currentUser, query) + const updatedAuthorsList = Array.isArray(authorsList) ? ([ ...(authorsList || []).map(item => ({ diff --git a/web/src/services/code/index.tsx b/web/src/services/code/index.tsx index fe769a986..bbf3e40e9 100644 --- a/web/src/services/code/index.tsx +++ b/web/src/services/code/index.tsx @@ -2580,6 +2580,36 @@ export const useListPrincipals = (props: UseListPrincipalsProps) => ...props }) +export interface GetPrincipalPathParams { + id: number +} + +export type GetPrincipalProps = Omit< + GetProps, + 'path' +> & + GetPrincipalPathParams + +export const GetPrincipal = ({ id, ...props }: GetPrincipalProps) => ( + + path={`/principals/${id}`} + base={getConfig('code/api/v1')} + {...props} + /> +) + +export type UseGetPrincipalProps = Omit< + UseGetProps, + 'path' +> & + GetPrincipalPathParams + +export const useGetPrincipal = ({ id, ...props }: UseGetPrincipalProps) => + useGet( + (paramsInPath: GetPrincipalPathParams) => `/principals/${paramsInPath.id}`, + { base: getConfig('code/api/v1'), pathParams: { id }, ...props } + ) + export interface OnRegisterQueryParams { /** * If set to true the token is also returned as a cookie. diff --git a/web/src/services/code/swagger.yaml b/web/src/services/code/swagger.yaml index a2cf55b46..8a9d96303 100644 --- a/web/src/services/code/swagger.yaml +++ b/web/src/services/code/swagger.yaml @@ -1042,6 +1042,54 @@ paths: description: Internal Server Error tags: - principals + /principals/{id}: + get: + operationId: getPrincipal + parameters: + - in: path + name: id + required: true + schema: + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TypesPrincipalInfo' + description: OK + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/UsererrorError' + description: Bad Request + '401': + content: + application/json: + schema: + $ref: '#/components/schemas/UsererrorError' + description: Unauthorized + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/UsererrorError' + description: Forbidden + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/UsererrorError' + description: Not Found + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/UsererrorError' + description: Internal Server Error + tags: + - principals /register: post: operationId: onRegister diff --git a/web/src/utils/Utils.ts b/web/src/utils/Utils.ts index c7c55a7cf..1b156307c 100644 --- a/web/src/utils/Utils.ts +++ b/web/src/utils/Utils.ts @@ -99,6 +99,7 @@ export const getErrorMessage = (error: Unknown): string | undefined => error ? get(error, 'data.error', get(error, 'data.message', get(error, 'message', error))) : undefined export interface PageBrowserProps { + author?: string page?: string state?: string tab?: string