diff --git a/backend/analytics_server/mhq/api/integrations.py b/backend/analytics_server/mhq/api/integrations.py index 35ac97d0b..c3055dd20 100644 --- a/backend/analytics_server/mhq/api/integrations.py +++ b/backend/analytics_server/mhq/api/integrations.py @@ -9,6 +9,8 @@ from mhq.service.query_validator import get_query_validator from mhq.store.models import UserIdentityProvider from mhq.utils.github import github_org_data_multi_thread_worker +from mhq.service.deployments.deployment_service import get_deployments_service +from mhq.store.models.code.workflows import RepoWorkflowProviders app = Blueprint("integrations", __name__) @@ -134,12 +136,33 @@ def get_workflows_for_repo(org_id: str, gh_org_name: str, gh_org_repo_name: str) { "id": github_workflow.id, "name": github_workflow.name, - "html_url": github_workflow.html_url, + "provider_workflow_id": github_workflow.id, } for github_workflow in workflows_list ] +@app.route("/orgs//integrations/webhook//workflows", methods={"GET"}) +def get_webhook_workflows(org_id: str, repo_id: str): + + query_validator = get_query_validator() + query_validator.org_validator(org_id) + + deployments_service = get_deployments_service() + repo_workflows = deployments_service.get_all_repo_workflows_by_repo_id_and_provider( + repo_id, RepoWorkflowProviders.WEBHOOK + ) + + return [ + { + "id": str(workflow.id), + "name": workflow.name, + "provider_workflow_id": workflow.provider_workflow_id, + } + for workflow in repo_workflows + ] + + @app.route("/orgs//integrations/gitlab/groups", methods={"GET"}) def get_gitlab_orgs(org_id: str): query_validator = get_query_validator() diff --git a/backend/analytics_server/mhq/service/deployments/deployment_service.py b/backend/analytics_server/mhq/service/deployments/deployment_service.py index 123cbd8c4..efdae702f 100644 --- a/backend/analytics_server/mhq/service/deployments/deployment_service.py +++ b/backend/analytics_server/mhq/service/deployments/deployment_service.py @@ -194,6 +194,15 @@ def save_repo_workflow_and_workflow_runs( repo_workflows, repo_workflow_runs ) + def get_all_repo_workflows_by_repo_id_and_provider( + self, repo_id: str, provider: RepoWorkflowProviders + ) -> List[RepoWorkflow]: + return ( + self.workflow_repo_service.get_all_repo_workflows_by_repo_id_and_provider( + repo_id, provider + ) + ) + def get_deployments_service() -> DeploymentsService: return DeploymentsService( diff --git a/backend/analytics_server/mhq/store/repos/workflows.py b/backend/analytics_server/mhq/store/repos/workflows.py index 254ac60f2..981b485b4 100644 --- a/backend/analytics_server/mhq/store/repos/workflows.py +++ b/backend/analytics_server/mhq/store/repos/workflows.py @@ -39,6 +39,20 @@ def get_active_repo_workflows_by_repo_ids_and_providers( .all() ) + def get_all_repo_workflows_by_repo_id_and_provider( + self, repo_id: str, provider: RepoWorkflowProviders + ) -> List[RepoWorkflow]: + + return ( + self._db.session.query(RepoWorkflow) + .options(defer(RepoWorkflow.meta)) + .filter( + RepoWorkflow.org_repo_id == repo_id, + RepoWorkflow.provider == provider, + ) + .all() + ) + def get_repo_workflow_by_provider_workflow_id( self, repo_id: str, provider: RepoWorkflowProviders, provider_workflow_id: str ) -> Optional[RepoWorkflow]: diff --git a/web-server/pages/api/internal/[org_id]/integrations/workflows.ts b/web-server/pages/api/internal/[org_id]/integrations/workflows.ts index 02687a9a7..a3c36503f 100644 --- a/web-server/pages/api/internal/[org_id]/integrations/workflows.ts +++ b/web-server/pages/api/internal/[org_id]/integrations/workflows.ts @@ -4,6 +4,7 @@ import { handleRequest } from '@/api-helpers/axios'; import { Endpoint } from '@/api-helpers/global'; import { CIProvider, Integration } from '@/constants/integrations'; import { RepoWorkflowResponse, RepoWorkflow } from '@/types/resources'; +import { db } from '@/utils/db'; const pathSchema = yup.object().shape({ org_id: yup.string().uuid().required() @@ -31,6 +32,25 @@ endpoint.handle.GET(getSchema, async (req, res) => { ) : Promise.resolve([]); + const org_repo = await db('OrgRepo') + .select('*') + .where('provider', provider) + .where('org_name', org_name) + .where('name', repo_name) + .first(); + + let webhookWorkflows: RepoWorkflow[] = []; + + if (org_repo) { + const webhookWorkflowsResponse = await handleRequest( + `/orgs/${org_id}/integrations/webhook/${org_repo.id}/workflows` + ); + webhookWorkflows = adaptWorkflows( + webhookWorkflowsResponse, + CIProvider.WEBHOOK + ); + } + let params: { repo_slug: string; page_token?: string } = { repo_slug: repo_slug }; @@ -51,7 +71,11 @@ endpoint.handle.GET(getSchema, async (req, res) => { .catch(() => ({ workflows: [], next_page_token: null })) ]); return res.send({ - workflows: [...githubActionWorkflows, ...circleciWorkflows.workflows], + workflows: [ + ...githubActionWorkflows, + ...circleciWorkflows.workflows, + ...webhookWorkflows + ], next_page_token: circleciWorkflows.next_page_token }); }); @@ -62,7 +86,7 @@ const adaptWorkflows = ( ) => { return repoWorkflows.map((workflows) => ({ ...workflows, - ci_provider: ciProvider + provider: ciProvider })); }; 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 bf0db917e..b9b7603ea 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 @@ -283,12 +283,12 @@ const updateReposWorkflows = async ( curr.repo_workflows?.map((w) => ({ value: String(w.value), name: w.name, - provider: curr.provider + provider: w.provider })) || [] }), {} as Record< string, - { name: string; value: string; provider: Integration }[] + { name: string; value: string; provider: CIProvider }[] > ); @@ -320,12 +320,7 @@ const updateReposWorkflows = async ( workflows.map((workflow) => ({ is_active: true, name: workflow.name, - provider: - workflow.provider === Integration.GITHUB - ? CIProvider.GITHUB_ACTIONS - : workflow.provider === Integration.BITBUCKET - ? CIProvider.CIRCLE_CI - : null, + provider: workflow.provider, provider_workflow_id: String(workflow.value), type: WorkflowType.DEPLOYMENT, org_repo_id: groupedRepos[repoName]?.id @@ -375,7 +370,8 @@ const getTeamReposMap = async (org_id: ID, providers: Integration[]) => { const repoWithWorkflows = dbRepos.map((repo) => { const updatedWorkflows = repo.repo_workflows.map((workflow) => ({ name: workflow.name, - value: workflow.provider_workflow_id + value: workflow.provider_workflow_id, + provider: workflow.provider })); return { diff --git a/web-server/src/components/WorkflowSelector.tsx b/web-server/src/components/WorkflowSelector.tsx index 3bf761a5b..6fa85d2b8 100644 --- a/web-server/src/components/WorkflowSelector.tsx +++ b/web-server/src/components/WorkflowSelector.tsx @@ -43,7 +43,8 @@ export const DeploymentWorkflowSelector: FC<{ repo: BaseRepo }> = ({ () => repo.repo_workflows?.map((val) => ({ label: val.name, - value: val.value + value: val.value, + provider: val.provider })) || [], [repo.repo_workflows] ); @@ -92,7 +93,8 @@ export const DeploymentWorkflowSelector: FC<{ repo: BaseRepo }> = ({ ), label: w.name, - value: w.id + value: w.provider_workflow_id, + provider: w.provider })); depFn(options.set, (prev) => union(prev, workflowOpts)); @@ -154,7 +156,11 @@ export const DeploymentWorkflowSelector: FC<{ repo: BaseRepo }> = ({ const updatedOptions = isChecked ? [ ...selectedOptions, - { label: o.label, value: String(o.value) } + { + label: o.label, + value: String(o.value), + provider: o.provider + } ] : selectedOptions.filter( (w) => w.value !== String(o.value) @@ -163,7 +169,8 @@ export const DeploymentWorkflowSelector: FC<{ repo: BaseRepo }> = ({ repo, updatedOptions.map((rw) => ({ name: rw.label, - value: rw.value + value: rw.value, + provider: rw.provider })) ); }} diff --git a/web-server/src/constants/integrations.ts b/web-server/src/constants/integrations.ts index 39a133f82..b73eb890e 100644 --- a/web-server/src/constants/integrations.ts +++ b/web-server/src/constants/integrations.ts @@ -15,7 +15,8 @@ export enum Integration { export enum CIProvider { GITHUB_ACTIONS = 'GITHUB_ACTIONS', - CIRCLE_CI = 'CIRCLE_CI' + CIRCLE_CI = 'CIRCLE_CI', + WEBHOOK = 'WEBHOOK' } export enum WorkflowType { diff --git a/web-server/src/types/resources.ts b/web-server/src/types/resources.ts index fa2d19e87..12df31cb1 100644 --- a/web-server/src/types/resources.ts +++ b/web-server/src/types/resources.ts @@ -202,12 +202,15 @@ export type RepoWorkflow = { id: number; name: string; html_url: string | null; - ci_provider: CIProvider; + provider: CIProvider; provider_workflow_id: string; value: string; }; -export type AdaptedRepoWorkflow = Pick; +export type AdaptedRepoWorkflow = Pick< + RepoWorkflow, + 'name' | 'value' | 'provider' +>; export type SelectedRepo = Row<'OrgRepo'> & { repo_workflow: Row<'RepoWorkflow'>;