diff --git a/web-server/libdefs/ambient.d.ts b/web-server/libdefs/ambient.d.ts index fa529dd9c..0ebb25d00 100644 --- a/web-server/libdefs/ambient.d.ts +++ b/web-server/libdefs/ambient.d.ts @@ -65,6 +65,7 @@ declare namespace NodeJS { NEXT_PUBLIC_BUILD_TIME: string; NEXT_PUBLIC_APP_ENVIRONMENT: 'production' | 'development'; INTERNAL_API_BASE_URL: string; + INTERNAL_SYNC_API_BASE_URL: string; SECRET_PUBLIC_KEY: string; SECRET_PRIVATE_KEY: string; DB_HOST: string; diff --git a/web-server/pages/api/internal/[org_id]/git_provider_org.ts b/web-server/pages/api/internal/[org_id]/git_provider_org.ts index 7d4af33f4..8f204e399 100644 --- a/web-server/pages/api/internal/[org_id]/git_provider_org.ts +++ b/web-server/pages/api/internal/[org_id]/git_provider_org.ts @@ -1,5 +1,4 @@ import { AxiosError } from 'axios'; -import { getTeamRepos } from 'pages/api/resources/team_repos'; import * as yup from 'yup'; import { internal } from '@/api-helpers/axios'; @@ -42,8 +41,7 @@ endpoint.handle.GET(getSchema, async (req, res) => { const response = await getRepos( org_id, provider as CodeSourceProvidersIntegration, - org_name, - team_id + org_name ); return res.status(200).send(response); }); @@ -72,25 +70,9 @@ export const getProviderOrgs = ( export const getRepos = async ( org_id: ID, provider: CodeSourceProvidersIntegration, - org_name: string, - team_id?: ID + org_name: string ) => { - let [repos, teamRepos] = await Promise.all([ - batchPaginatedListsRequest( - `/orgs/${org_id}/integrations/${provider}/${providerOrgBrandingMap[provider]}/${org_name}/repos` - ).then((rs) => rs.map(getBaseRepoFromUnionRepo)), - getTeamRepos(team_id) - ]); - - const teamReposSet = new Set( - teamRepos.map((tr) => `${tr.org_name}/${tr.name}`) - ); - - if (team_id) { - repos = repos.filter((repo) => - teamReposSet.has(`${repo.parent}/${repo.name}`) - ); - } - - return repos; + return await batchPaginatedListsRequest( + `/orgs/${org_id}/integrations/${provider}/${providerOrgBrandingMap[provider]}/${org_name}/repos` + ).then((rs) => rs.map(getBaseRepoFromUnionRepo)); }; diff --git a/web-server/pages/api/internal/[org_id]/sync_repos.ts b/web-server/pages/api/internal/[org_id]/sync_repos.ts index 8275a8e62..1ab25f3e9 100644 --- a/web-server/pages/api/internal/[org_id]/sync_repos.ts +++ b/web-server/pages/api/internal/[org_id]/sync_repos.ts @@ -1,6 +1,6 @@ import * as yup from 'yup'; -import { handleRequest } from '@/api-helpers/axios'; +import { handleSyncServerRequest } from '@/api-helpers/axios'; import { Endpoint, nullSchema } from '@/api-helpers/global'; const pathSchema = yup.object().shape({ @@ -18,9 +18,6 @@ endpoint.handle.POST(nullSchema, async (req, res) => { }); export const syncReposForOrg = (org_id: ID) => - Promise.all([ - handleRequest(`/orgs/${org_id}/sync_repos`, { method: 'POST' }), - handleRequest(`/orgs/${org_id}/sync_workflows`, { method: 'POST' }) - ]); + handleSyncServerRequest(`/orgs/${org_id}/sync`, { method: 'POST' }); export default endpoint.serve(); diff --git a/web-server/pages/api/internal/team/[team_id]/get_incidents.ts b/web-server/pages/api/internal/team/[team_id]/get_incidents.ts index 24622cb8a..b3c19abb8 100644 --- a/web-server/pages/api/internal/team/[team_id]/get_incidents.ts +++ b/web-server/pages/api/internal/team/[team_id]/get_incidents.ts @@ -48,7 +48,7 @@ endpoint.handle.GET(getSchema, async (req, res) => { await getAllTeamsReposProdBranchesForOrgAsMap(org_id); const teamRepoFiltersMap = repoFiltersFromTeamProdBranches(teamProdBranchesMap); - const pr_filter = await updatePrFilterParams( + const prFilter = await updatePrFilterParams( team_id, {}, { @@ -63,7 +63,7 @@ endpoint.handle.GET(getSchema, async (req, res) => { team_id, from_date, to_date, - pr_filter + pr_filter: prFilter.pr_filter }); return res.send({ diff --git a/web-server/pages/api/resources/orgs/[org_id]/teams/v2.ts b/web-server/pages/api/resources/orgs/[org_id]/teams/v2.ts index 51b89d949..182cc9205 100644 --- a/web-server/pages/api/resources/orgs/[org_id]/teams/v2.ts +++ b/web-server/pages/api/resources/orgs/[org_id]/teams/v2.ts @@ -1,7 +1,6 @@ import { groupBy, prop, mapObjIndexed, forEachObjIndexed } from 'ramda'; import * as yup from 'yup'; -import { enableOrgReposIfNotEnabled } from '@/api/integrations/orgs'; import { CodeSourceProvidersIntegration, getProviderOrgs, @@ -12,12 +11,13 @@ import { updateOnBoardingState } from '@/api/resources/orgs/[org_id]/onboarding'; import { getTeamRepos } from '@/api/resources/team_repos'; +import { handleRequest } from '@/api-helpers/axios'; import { Endpoint } from '@/api-helpers/global'; -import { Columns, Table } from '@/constants/db'; +import { Row } from '@/constants/db'; import { Integration } from '@/constants/integrations'; import { getTeamV2Mock } from '@/mocks/teams'; import { BaseTeam } from '@/types/api/teams'; -import { OnboardingStep, ReqOrgRepo } from '@/types/resources'; +import { OnboardingStep, ReqRepoWithProvider } from '@/types/resources'; import { db, getFirstRow } from '@/utils/db'; const getSchema = yup.object().shape({ @@ -111,29 +111,38 @@ endpoint.handle.POST(postSchema, async (req, res) => { } const { org_repos, org_id, provider, name } = req.payload; - const orgReposList: ReqOrgRepo[] = []; - forEachObjIndexed( - (repos, org) => orgReposList.push({ org, repos }), - org_repos - ); - - const [team, repos, onboardingState] = await Promise.all([ + const orgReposList: ReqRepoWithProvider[] = []; + forEachObjIndexed((repos, org) => { + repos.forEach((repo) => { + orgReposList.push({ + ...repo, + org, + provider + } as any as ReqRepoWithProvider); + }); + }, org_repos); + const [team, onboardingState] = await Promise.all([ createTeam(org_id, name, []), - enableOrgReposIfNotEnabled(org_id, provider as Integration, orgReposList), getOnBoardingState(org_id) ]); + const updatedOnboardingState = Array.from( new Set(onboardingState.onboarding_state).add(OnboardingStep.TEAM_CREATED) ); const [teamRepos] = await Promise.all([ - addReposToTeam( - team.id, - repos.map((r) => r.id) - ), + handleRequest<(Row<'TeamRepos'> & Row<'OrgRepo'>)[]>( + `/teams/${team.id}/repos`, + { + method: 'PUT', + data: { + repos: orgReposList + } + } + ).then((repos) => repos.map((r) => ({ ...r, team_id: team.id }))), updateOnBoardingState(org_id, updatedOnboardingState) ]); - res.send({ team, teamReposMap: groupBy(prop('team_id'), teamRepos), repos }); + res.send({ team, teamReposMap: groupBy(prop('team_id'), teamRepos) }); }); endpoint.handle.PATCH(patchSchema, async (req, res) => { @@ -141,33 +150,27 @@ endpoint.handle.PATCH(patchSchema, async (req, res) => { return res.send(getTeamV2Mock); } - const { org_id, id, name, org_repos, provider } = req.payload; - const orgReposList: ReqOrgRepo[] = []; - forEachObjIndexed( - (repos, org) => orgReposList.push({ org, repos }), - org_repos - ); - - const upsertPayload: Record = { - id, - org_id, - updated_at: new Date(), - manager_id: null - }; - - if (name) { - upsertPayload.name = name; - } - - const [team, repos] = await Promise.all([ + const { id, name, org_repos, provider } = req.payload; + const orgReposList: ReqRepoWithProvider[] = []; + forEachObjIndexed((repos, org) => { + repos.forEach((repo) => { + orgReposList.push({ + ...repo, + org, + provider + } as any as ReqRepoWithProvider); + }); + }, org_repos); + + const [team, teamRepos] = await Promise.all([ updateTeam(id, name, []), - enableOrgReposIfNotEnabled(org_id, provider as Integration, orgReposList) + handleRequest<(Row<'TeamRepos'> & Row<'OrgRepo'>)[]>(`/teams/${id}/repos`, { + method: 'PUT', + data: { + repos: orgReposList + } + }).then((repos) => repos.map((r) => ({ ...r, team_id: id }))) ]); - const teamRepos = await addReposToTeam( - id, - repos.map((r) => r.id) - ); - res.send({ team, teamReposMap: groupBy(prop('team_id'), teamRepos) }); }); @@ -193,18 +196,13 @@ const updateTeam = async ( team_name: string, member_ids: string[] ): Promise => { - return db('Team') - .insert({ - id: team_id, + return handleRequest(`/team/${team_id}`, { + method: 'PATCH', + data: { name: team_name, - member_ids: member_ids, - is_deleted: false, - updated_at: new Date() - }) - .onConflict('id') - .merge() - .returning('*') - .then(getFirstRow); + member_ids + } + }); }; const createTeam = async ( @@ -212,48 +210,13 @@ const createTeam = async ( team_name: string, member_ids: string[] ): Promise => { - return db(Table.Team) - .insert({ - org_id, + return handleRequest(`/org/${org_id}/team`, { + method: 'POST', + data: { name: team_name, - member_ids: member_ids, - is_deleted: false - }) - .returning('*') - .then(getFirstRow); -}; - -const addReposToTeam = async (team_id: ID, repo_ids: ID[]) => { - try { - await db(Table.TeamRepos) - .update({ - [Columns[Table.TeamRepos].is_active]: false, - updated_at: new Date() - }) - .where('team_id', team_id); - } catch (err) {} - - try { - const payload = repo_ids.map((repo_id) => ({ - [Columns[Table.TeamRepos].team_id]: team_id, - [Columns[Table.TeamRepos].org_repo_id]: repo_id, - [Columns[Table.TeamRepos].is_active]: true - })); - const data = - payload.length && - (await db('TeamRepos') - .insert( - repo_ids.map((repo_id) => ({ - [Columns[Table.TeamRepos].team_id]: team_id, - [Columns[Table.TeamRepos].org_repo_id]: repo_id, - [Columns[Table.TeamRepos].is_active]: true - })) - ) - .onConflict(['team_id', 'org_repo_id']) - .merge() - .returning('*')); - return data; - } catch (err) {} + member_ids + } + }); }; export const getAllOrgRepos = async ( diff --git a/web-server/pages/api/resources/team_repos.ts b/web-server/pages/api/resources/team_repos.ts index 729c84e0b..7548be722 100644 --- a/web-server/pages/api/resources/team_repos.ts +++ b/web-server/pages/api/resources/team_repos.ts @@ -27,14 +27,9 @@ endpoint.handle.GET(getSchema, async (req, res) => { }); export const getTeamRepos = (team_id: ID) => - (team_id - ? db('TeamRepos') - .select('*', 'OrgRepo.* as org_repo') - .leftJoin('OrgRepo', 'OrgRepo.id', 'TeamRepos.org_repo_id') - .where('TeamRepos.is_active', true) - .andWhere('TeamRepos.team_id', team_id) - .orderBy('name', 'asc') - : []) as any as Promise<(Row<'TeamRepos'> & Row<'OrgRepo'>)[]>; + handleRequest<(Row<'TeamRepos'> & Row<'OrgRepo'>)[]>( + `/teams/${team_id}/repos` + ).then((repos) => repos.map((r) => ({ ...r, team_id: team_id }))); endpoint.handle.PATCH(patchSchema, async (req, res) => { if (req.meta?.features?.use_mock_data) { diff --git a/web-server/pages/integrations.tsx b/web-server/pages/integrations.tsx index 7b03d2dc0..72c407603 100644 --- a/web-server/pages/integrations.tsx +++ b/web-server/pages/integrations.tsx @@ -7,6 +7,7 @@ import { FlexBox } from '@/components/FlexBox'; import { Line } from '@/components/Text'; import { Integration } from '@/constants/integrations'; import { ROUTES } from '@/constants/routes'; +import { FetchState } from '@/constants/ui-states'; import { GithubIntegrationCard } from '@/content/Dashboards/IntegrationCards'; import { PageWrapper } from '@/content/PullRequests/PageWrapper'; import { useAuth } from '@/hooks/useAuth'; @@ -18,6 +19,10 @@ import { PageLayout } from '@/types/resources'; import { depFn } from '@/utils/fn'; function Integrations() { + const isLoading = useSelector( + (s) => s.team.requests?.teams === FetchState.REQUEST + ); + return ( <> @@ -51,6 +57,9 @@ const Content = () => { const dispatch = useDispatch(); const loading = useBoolState(false); + const teamCount = teams.length; + const showCreationCTA = isLinked && !teamCount && !loading.value; + useEffect(() => { if (!orgId) return; depFn(loading.true); @@ -62,14 +71,13 @@ const Content = () => { ).finally(loading.false); }, [dispatch, loading.false, loading.true, orgId]); - const teamCount = teams.length; return ( Integrate your Github to fetch DORA for your team - {Boolean(teamCount) && ( + {Boolean(teamCount) && Boolean(isLinked) && (