Skip to main content
Glama

mcp-github-project-manager

GitHubAutomationRuleRepository.ts12.4 kB
import { BaseGitHubRepository } from "./BaseRepository"; import { AutomationAction, AutomationCondition, AutomationRule, AutomationRuleRepository, AutomationTrigger, CreateAutomationRule, AutomationTriggerType, AutomationActionType } from "../../../domain/automation-types"; import { CreateProjectRuleResponse, DeleteProjectRuleResponse, GetProjectRuleResponse, GitHubProjectRuleNode, ListProjectRulesResponse, UpdateProjectRuleResponse, mapFromGitHubActionType, mapFromGitHubTriggerType, mapToGitHubActionType, mapToGitHubTriggerType } from "../automation-types"; import { ProjectId } from "../../../domain/types"; export class GitHubAutomationRuleRepository extends BaseGitHubRepository implements AutomationRuleRepository { async create(data: CreateAutomationRule): Promise<AutomationRule> { const mutation = ` mutation($input: CreateProjectV2RuleInput!) { createProjectV2Rule(input: $input) { projectRule { id databaseId createdAt updatedAt isActive name ruleTrigger { type whenStateEquals whenStateWas whenFieldValueEquals whenFieldId } ruleActions { type fieldId value projectItemId sourceFieldId targetFieldId labelName milestoneId } } } } `; try { const variables = this.prepareCreateRuleVariables(data); const response = await this.graphql<CreateProjectRuleResponse>(mutation, variables); return this.mapGitHubRuleToAutomationRule(response.createProjectV2Rule.projectRule, data.projectId); } catch (error) { this.logger.error(`Failed to create automation rule for project ${data.projectId}`, error); throw this.handleGraphQLError(error); } } async update(id: string, data: Partial<AutomationRule>): Promise<AutomationRule> { const mutation = ` mutation($input: UpdateProjectV2RuleInput!) { updateProjectV2Rule(input: $input) { projectRule { id databaseId createdAt updatedAt isActive name ruleTrigger { type whenStateEquals whenStateWas whenFieldValueEquals whenFieldId } ruleActions { type fieldId value projectItemId sourceFieldId targetFieldId labelName milestoneId } } } } `; try { const variables = { input: { ruleId: id, name: data.name, isActive: data.enabled, } }; const response = await this.graphql<UpdateProjectRuleResponse>(mutation, variables); return this.mapGitHubRuleToAutomationRule( response.updateProjectV2Rule.projectRule, data.projectId || "" ); } catch (error) { this.logger.error(`Failed to update automation rule ${id}`, error); throw this.handleGraphQLError(error); } } async delete(id: string): Promise<void> { const mutation = ` mutation($input: DeleteProjectV2RuleInput!) { deleteProjectV2Rule(input: $input) { projectRule { id } } } `; try { await this.graphql<DeleteProjectRuleResponse>(mutation, { input: { ruleId: id } }); } catch (error) { this.logger.error(`Failed to delete automation rule ${id}`, error); throw this.handleGraphQLError(error); } } async findById(id: string): Promise<AutomationRule | null> { const query = ` query($id: ID!) { node(id: $id) { ... on ProjectV2Rule { id databaseId createdAt updatedAt isActive name ruleTrigger { type whenStateEquals whenStateWas whenFieldValueEquals whenFieldId } ruleActions { type fieldId value projectItemId sourceFieldId targetFieldId labelName milestoneId } } } } `; try { const response = await this.graphql<GetProjectRuleResponse>(query, { id }); if (!response.node) return null; // We need to determine the projectId const projectId = await this.getProjectIdForRule(id); return this.mapGitHubRuleToAutomationRule(response.node, projectId); } catch (error) { this.logger.error(`Failed to fetch automation rule ${id}`, error); throw this.handleGraphQLError(error); } } async findByProject(projectId: ProjectId): Promise<AutomationRule[]> { const query = ` query($projectId: ID!) { node(id: $projectId) { ... on ProjectV2 { projectRules(first: 100) { nodes { id databaseId createdAt updatedAt isActive name ruleTrigger { type whenStateEquals whenStateWas whenFieldValueEquals whenFieldId } ruleActions { type fieldId value projectItemId sourceFieldId targetFieldId labelName milestoneId } } } } } } `; try { const response = await this.graphql<ListProjectRulesResponse>(query, { projectId }); if (!response.node?.projectRules?.nodes) return []; return response.node.projectRules.nodes.map(rule => this.mapGitHubRuleToAutomationRule(rule, projectId) ); } catch (error) { this.logger.error(`Failed to fetch automation rules for project ${projectId}`, error); throw this.handleGraphQLError(error); } } async enable(id: string): Promise<AutomationRule> { const rule = await this.findById(id); if (!rule) throw new Error(`Automation rule with ID ${id} not found`); return await this.update(id, { enabled: true }); } async disable(id: string): Promise<AutomationRule> { const rule = await this.findById(id); if (!rule) throw new Error(`Automation rule with ID ${id} not found`); return await this.update(id, { enabled: false }); } private async getProjectIdForRule(ruleId: string): Promise<string> { // This is a simplified implementation. In real-world scenarios, // we'd need to query the API to determine which project the rule belongs to // or maintain a local cache mapping rules to projects const query = ` query($ruleId: ID!) { node(id: $ruleId) { ... on ProjectV2Rule { project { id } } } } `; try { const response = await this.graphql<any>(query, { ruleId }); return response.node?.project?.id || ""; } catch (error) { this.logger.error(`Failed to determine project ID for rule ${ruleId}`, error); return ""; } } private prepareCreateRuleVariables(data: CreateAutomationRule): any { const variables: any = { input: { projectId: data.projectId, name: data.name, isActive: data.enabled === undefined ? true : data.enabled } }; // Map trigger - use the first trigger from the triggers array if (data.triggers && data.triggers.length > 0) { const trigger = data.triggers[0]; // GitHub API allows one trigger per rule variables.input.triggerType = mapToGitHubTriggerType(trigger.type); // Map conditions if (trigger.conditions && trigger.conditions.length > 0) { const condition = trigger.conditions[0]; // GitHub only supports one condition per rule if (trigger.type === AutomationTriggerType.RESOURCE_UPDATED && condition.field) { variables.input.whenFieldId = condition.field; if (condition.operator === 'equals' && condition.value) { variables.input.whenFieldValueEquals = String(condition.value); } } } } // Map actions if (data.actions && data.actions.length > 0) { const action = data.actions[0]; // GitHub API expects actions to be passed one at a time variables.input.actionType = mapToGitHubActionType(action.type); // Get action parameters const params = action.parameters || {}; if (action.type === AutomationActionType.UPDATE_RESOURCE) { variables.input.fieldId = params.fieldId; variables.input.value = String(params.value); } else if (action.type === AutomationActionType.CUSTOM_SCRIPT) { variables.input.sourceFieldId = params.sourceFieldId; variables.input.targetFieldId = params.targetFieldId; } else if ((action.type === AutomationActionType.ADD_LABEL || action.type === AutomationActionType.REMOVE_LABEL) && params.labelName) { variables.input.labelName = params.labelName; } else if (params.milestoneId) { // Handle milestone separately from the type check to avoid type errors variables.input.milestoneId = params.milestoneId; } } return variables; } private mapGitHubRuleToAutomationRule(rule: GitHubProjectRuleNode, projectId: string): AutomationRule { // Map trigger and conditions const trigger: AutomationTrigger = { id: `trigger_${Date.now()}`, // Generate a unique ID type: mapFromGitHubTriggerType(rule.ruleTrigger.type), conditions: [] // Initialize with empty array to avoid undefined conditions }; // Add condition if available if (rule.ruleTrigger.whenFieldId || rule.ruleTrigger.whenFieldValueEquals || rule.ruleTrigger.whenStateEquals || rule.ruleTrigger.whenStateWas) { const condition: AutomationCondition = { id: `condition_${Date.now()}`, // Generate a unique ID field: rule.ruleTrigger.whenFieldId || '', operator: 'equals', // Default operator value: rule.ruleTrigger.whenFieldValueEquals || rule.ruleTrigger.whenStateEquals || null }; // Safe to push now that we know conditions is an array if (trigger.conditions) { trigger.conditions.push(condition); } } // Map actions const actions: AutomationAction[] = rule.ruleActions.map(action => { // Create a base action with required properties const domainAction: AutomationAction = { id: `action_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, // Generate a unique ID type: mapFromGitHubActionType(action.type), parameters: {} // Initialize empty parameters object }; // Add parameters based on available properties if (action.fieldId) domainAction.parameters.fieldId = action.fieldId; if (action.value) domainAction.parameters.value = action.value; if (action.sourceFieldId) domainAction.parameters.sourceFieldId = action.sourceFieldId; if (action.targetFieldId) domainAction.parameters.targetFieldId = action.targetFieldId; if (action.labelName) domainAction.parameters.labelName = action.labelName; if (action.milestoneId) domainAction.parameters.milestoneId = action.milestoneId; return domainAction; }); return { id: rule.id, projectId, name: rule.name, description: `Rule created from GitHub Project Rule ${rule.id}`, enabled: rule.isActive, createdAt: new Date(rule.createdAt), // Convert string to Date updatedAt: rule.updatedAt ? new Date(rule.updatedAt) : undefined, // Convert string to Date triggers: [trigger], // Use the triggers array instead of a single trigger actions }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kunwarVivek/mcp-github-project-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server