chore: add github basic apis
This commit is contained in:
@@ -1,12 +1,155 @@
|
||||
class Api::V1::Accounts::Integrations::GithubController < Api::V1::Accounts::BaseController
|
||||
before_action :fetch_hook, only: [:destroy]
|
||||
before_action :fetch_hook
|
||||
before_action :ensure_hook_exists, except: [:destroy]
|
||||
|
||||
def destroy
|
||||
@hook.destroy!
|
||||
head :ok
|
||||
end
|
||||
|
||||
def repositories
|
||||
repositories = github_service.repositories
|
||||
filtered_repos = repositories.map do |repo|
|
||||
{
|
||||
full_name: repo[:full_name] || repo.full_name,
|
||||
private: repo[:private] || repo.private
|
||||
}
|
||||
end
|
||||
render json: filtered_repos
|
||||
end
|
||||
|
||||
def search_repositories
|
||||
repositories = github_service.search_repositories(params[:q])
|
||||
filtered_repos = repositories.map do |repo|
|
||||
{
|
||||
full_name: repo[:full_name] || repo.full_name,
|
||||
private: repo[:private] || repo.private
|
||||
}
|
||||
end
|
||||
render json: filtered_repos
|
||||
end
|
||||
|
||||
def assignees
|
||||
assignees = github_service.assignees(repo_full_name)
|
||||
filtered_assignees = assignees.map do |assignee|
|
||||
{
|
||||
login: assignee.login,
|
||||
avatar_url: assignee.avatar_url
|
||||
}
|
||||
end
|
||||
render json: filtered_assignees
|
||||
end
|
||||
|
||||
def labels
|
||||
labels = github_service.labels(repo_full_name)
|
||||
filtered_labels = labels.map do |label|
|
||||
{
|
||||
name: label.name,
|
||||
color: label.color
|
||||
}
|
||||
end
|
||||
render json: filtered_labels
|
||||
end
|
||||
|
||||
def search_issues
|
||||
issues = github_service.search_issues(repo_full_name, params[:q])
|
||||
filtered_issues = issues.map do |issue|
|
||||
{
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
html_url: issue.html_url
|
||||
}
|
||||
end
|
||||
render json: filtered_issues
|
||||
end
|
||||
|
||||
def create_issue
|
||||
issue_data = github_service.create_issue(
|
||||
repo_full_name,
|
||||
params[:title],
|
||||
params[:body],
|
||||
issue_options
|
||||
)
|
||||
|
||||
github_issue = create_linked_issue(issue_data)
|
||||
render json: issue_response(github_issue), status: :created
|
||||
end
|
||||
|
||||
def link_issue
|
||||
issue_data = github_service.issue(repo_full_name, params[:issue_number])
|
||||
github_issue = create_linked_issue(issue_data)
|
||||
render json: issue_response(github_issue), status: :created
|
||||
end
|
||||
|
||||
def linked_issues
|
||||
issues = GithubIssue.where(conversation_id: params[:conversation_id])
|
||||
|
||||
render json: issues.map do |issue|
|
||||
{
|
||||
id: issue.id,
|
||||
issue_number: issue.issue_number,
|
||||
title: issue.issue_title,
|
||||
html_url: issue.html_url,
|
||||
repo_full_name: issue.repo_full_name,
|
||||
linked_by: {
|
||||
id: issue.linked_by.id,
|
||||
name: issue.linked_by.name
|
||||
},
|
||||
created_at: issue.created_at
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def unlink_issue
|
||||
github_issue = GithubIssue.find(params[:id])
|
||||
github_issue.destroy!
|
||||
head :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_hook
|
||||
@hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'github')
|
||||
end
|
||||
|
||||
def ensure_hook_exists
|
||||
render json: { error: 'GitHub integration not configured' }, status: :unauthorized unless @hook
|
||||
end
|
||||
|
||||
def github_service
|
||||
@github_service ||= Github::GithubService.new(hook: @hook)
|
||||
end
|
||||
|
||||
def repo_full_name
|
||||
params[:repo_full_name] || "#{params[:owner]}/#{params[:repo]}"
|
||||
end
|
||||
|
||||
def issue_options
|
||||
options = {}
|
||||
options[:assignees] = params[:assignees] if params[:assignees].present?
|
||||
options[:labels] = params[:labels] if params[:labels].present?
|
||||
options
|
||||
end
|
||||
|
||||
def create_linked_issue(issue_data)
|
||||
GithubIssue.create!(
|
||||
conversation_id: params[:conversation_id],
|
||||
account: Current.account,
|
||||
repo_full_name: repo_full_name,
|
||||
issue_number: issue_data.number,
|
||||
issue_title: issue_data.title,
|
||||
html_url: issue_data.html_url,
|
||||
linked_by: Current.user
|
||||
)
|
||||
end
|
||||
|
||||
def issue_response(github_issue)
|
||||
{
|
||||
id: github_issue.id,
|
||||
issue_number: github_issue.issue_number,
|
||||
title: github_issue.issue_title,
|
||||
html_url: github_issue.html_url,
|
||||
repo_full_name: github_issue.repo_full_name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
|
||||
'email' => ['MAILER_INBOUND_EMAIL_DOMAIN'],
|
||||
'linear' => %w[LINEAR_CLIENT_ID LINEAR_CLIENT_SECRET],
|
||||
'slack' => %w[SLACK_CLIENT_ID SLACK_CLIENT_SECRET],
|
||||
'github' => %w[GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET]
|
||||
'github' => %w[GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET],
|
||||
'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET],
|
||||
'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
class Github::GithubService
|
||||
attr_reader :hook
|
||||
|
||||
def initialize(hook:)
|
||||
@hook = hook
|
||||
end
|
||||
|
||||
def repositories
|
||||
client.repositories(nil, type: 'all')
|
||||
rescue Octokit::Error => e
|
||||
Rails.logger.error("GitHub API error fetching repositories: #{e.message}")
|
||||
raise_api_error(e)
|
||||
end
|
||||
|
||||
def assignees(repo_full_name)
|
||||
client.repository_assignees(repo_full_name)
|
||||
rescue Octokit::Error => e
|
||||
Rails.logger.error("GitHub API error fetching assignees for #{repo_full_name}: #{e.message}")
|
||||
raise_api_error(e)
|
||||
end
|
||||
|
||||
def search_repositories(query)
|
||||
return repositories if query.blank?
|
||||
|
||||
# Get all user's repositories first
|
||||
user_repos = repositories
|
||||
|
||||
# Filter repositories by query string (case-insensitive)
|
||||
user_repos.select do |repo|
|
||||
repo.full_name.downcase.include?(query.downcase) ||
|
||||
(repo.description&.downcase&.include?(query.downcase))
|
||||
end
|
||||
rescue Octokit::Error => e
|
||||
Rails.logger.error("GitHub API error searching repositories with query '#{query}': #{e.message}")
|
||||
raise_api_error(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def client
|
||||
@client ||= Octokit::Client.new(
|
||||
access_token: hook.access_token,
|
||||
auto_paginate: true,
|
||||
per_page: 100
|
||||
)
|
||||
end
|
||||
|
||||
def raise_api_error(error)
|
||||
case error.response_status
|
||||
when 401
|
||||
raise CustomExceptions::Base.new('GitHub authentication failed', 401)
|
||||
when 403
|
||||
raise CustomExceptions::Base.new('GitHub API rate limit exceeded or insufficient permissions', 403)
|
||||
when 404
|
||||
raise CustomExceptions::Base.new('GitHub resource not found', 404)
|
||||
else
|
||||
raise CustomExceptions::Base.new("GitHub API error: #{error.message}", error.response_status || 500)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -277,6 +277,9 @@ Rails.application.routes.draw do
|
||||
resource :github, controller: 'github', only: [] do
|
||||
collection do
|
||||
delete :destroy
|
||||
get :repositories
|
||||
get :search_repositories
|
||||
get 'repositories/:owner/:repo/assignees', to: 'github#assignees', as: :repository_assignees
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user