Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simplify github fetch and render errors inline #23

Merged
merged 4 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lua/pipeline/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ local defaultConfig = {
},
---@class pipeline.config.Highlights
highlights = {
---@type vim.api.keyset.highlight
PipelineError = { link = 'LspDiagnosticsVirtualTextError' },
---@type vim.api.keyset.highlight
PipelineRunIconSuccess = { link = 'LspDiagnosticsVirtualTextHint' },
---@type vim.api.keyset.highlight
Expand Down
146 changes: 62 additions & 84 deletions lua/pipeline/providers/github/rest/_api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,26 @@ end
local M = {}

---@class pipeline.providers.github.rest.FetchOptions
---@field method 'get'|'patch'|'post'|'put'|'delete'
---@field method? 'get'|'patch'|'post'|'put'|'delete'
---@field callback fun(err: plenary.curl.Error|nil, response: table|nil)

---@param server string
---@param path string
---@param opts? pipeline.providers.github.rest.FetchOptions
function M.fetch(server, path, opts)
---@param opts pipeline.providers.github.rest.FetchOptions
local function fetch(server, path, opts)
if vim.fn.has('nvim-0.11') == 1 then
vim.validate('server', server, 'string')
vim.validate('path', path, 'string')
vim.validate('opts', opts, 'table', true)
vim.validate('opts', opts, 'table', 'opts must be a table')
else
vim.validate {
server = { server, 'string' },
path = { path, 'string' },
opts = { opts, 'table', true },
opts = { opts, 'table' },
}
end

opts = opts or {}
opts.callback = opts.callback and vim.schedule_wrap(opts.callback)
opts.callback = vim.schedule_wrap(opts.callback)

local url = string.format('https://api.github.com%s', path)
if server ~= 'github.com' then
Expand Down Expand Up @@ -57,6 +56,38 @@ function M.fetch(server, path, opts)
)
end

---@class pipeline.providers.github.rest.FetchJsonOptions: pipeline.providers.github.rest.FetchOptions
---@field map_response? fun(response: table): any

---@param server string
---@param path string
---@param opts pipeline.providers.github.rest.FetchJsonOptions
local function fetch_json(server, path, opts)
return fetch(
server,
path,
vim.tbl_extend('force', opts, {
callback = function(err, response)
if err then
return opts.callback(err, nil)
end

if not response then
return opts.callback({ message = 'No response body' }, nil)
end

local response_data = vim.json.decode(response.body)

if opts.map_response then
response_data = opts.map_response(response_data)
end

opts.callback(nil, response_data)
end,
})
)
end

---@class GhWorkflow
---@field id number
---@field node_id string
Expand All @@ -75,29 +106,15 @@ end

---@param server string
---@param repo string
---@param opts? { callback?: fun(workflows: GhWorkflow[]): any }
---@param opts { callback: fun(error: plenary.curl.Error|nil, workflows: GhWorkflow[]): any }
function M.get_workflows(server, repo, opts)
opts = opts or {}

return M.fetch(
return fetch_json(
server,
string.format('/repos/%s/actions/workflows', repo),
vim.tbl_deep_extend('force', opts, {
callback = function(err, response)
if err or not response then
return {}
end

---@type GhWorkflowsResponse | nil
local response_data = vim.json.decode(response.body)

local ret = response_data and response_data.workflows or {}

if opts.callback then
return opts.callback(ret)
else
return ret
end
---@param response GhWorkflowsResponse
map_response = function(response)
return response.workflows
end,
})
)
Expand All @@ -121,43 +138,19 @@ end
---@field total_count number
---@field workflow_runs GhWorkflowRun[]

---@param opts? { callback?: fun(workflow_runs: GhWorkflowRun[]): any }
local function process_workflow_runs_response(opts)
opts = opts or {}

---@param err plenary.curl.Error | nil
---@param response table
---@return GhWorkflowRunsResponse
return function(err, response)
if err or not response then
return {}
end

---@type GhWorkflowRunsResponse | nil
local response_data = vim.json.decode(response.body)

local ret = (response_data and response_data.workflow_runs or {})

if opts.callback then
return opts.callback(ret)
else
return ret
end
end
end

---@param server string
---@param repo string
---@param per_page? integer
---@param opts? { callback?: fun(workflow_runs: GhWorkflowRun[]): any }
---@param opts { callback: fun(err: plenary.curl.Error|nil, workflow_runs: GhWorkflowRun[]): any }
function M.get_repository_workflow_runs(server, repo, per_page, opts)
opts = opts or {}

return M.fetch(
return fetch_json(
server,
string.format('/repos/%s/actions/runs', repo),
vim.tbl_deep_extend('force', { query = { per_page = per_page } }, opts, {
callback = process_workflow_runs_response(opts),
---@param response GhWorkflowRunsResponse
map_response = function(response)
return response.workflow_runs
end,
})
)
end
Expand All @@ -166,15 +159,16 @@ end
---@param repo string
---@param workflow_id integer|string
---@param per_page? integer
---@param opts? { callback?: fun(workflow_runs: GhWorkflowRun[]): any }
---@param opts { callback: fun(err: plenary.curl.Error|nil, workflow_runs: GhWorkflowRun[]): any }
function M.get_workflow_runs(server, repo, workflow_id, per_page, opts)
opts = opts or {}

return M.fetch(
return fetch_json(
server,
string.format('/repos/%s/actions/workflows/%d/runs', repo, workflow_id),
vim.tbl_deep_extend('force', { query = { per_page = per_page } }, opts, {
callback = process_workflow_runs_response(opts),
---@param response GhWorkflowRunsResponse
map_response = function(response)
return response.workflow_runs
end,
})
)
end
Expand All @@ -183,11 +177,9 @@ end
---@param repo string
---@param workflow_id integer|string
---@param ref string
---@param opts? table
---@param opts { body: table, callback?: fun(err: plenary.curl.Error|nil, response: unknown): any }
function M.dispatch_workflow(server, repo, workflow_id, ref, opts)
opts = opts or {}

return M.fetch(
return fetch(
server,
string.format(
'/repos/%s/actions/workflows/%d/dispatches',
Expand Down Expand Up @@ -227,29 +219,15 @@ end
---@param repo string
---@param workflow_run_id integer
---@param per_page? integer
---@param opts? { callback?: fun(workflow_runs: GhWorkflowRunJob[]): any }
---@param opts { callback: fun(err: plenary.curl.Error|nil, workflow_runs: GhWorkflowRunJob[]): any }
function M.get_workflow_run_jobs(server, repo, workflow_run_id, per_page, opts)
opts = opts or {}

return M.fetch(
return fetch_json(
server,
string.format('/repos/%s/actions/runs/%d/jobs', repo, workflow_run_id),
vim.tbl_deep_extend('force', { query = { per_page = per_page } }, opts, {
callback = function(err, response)
if err or not response then
return {}
end

---@type GhWorkflowRunJobsResponse | nil
local response_data = vim.json.decode(response.body)

local ret = response_data and response_data.jobs or {}

if opts.callback then
return opts.callback(ret)
else
return ret
end
---@param response GhWorkflowRunJobsResponse
map_response = function(response)
return response.jobs
end,
})
)
Expand Down
99 changes: 66 additions & 33 deletions lua/pipeline/providers/github/rest/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,50 @@ end
--TODO Maybe send lsp progress events when fetching, to interact
-- with fidget.nvim
function GithubRestProvider:fetch()
self:fetch_workflows()
self:fetch_runs()
end

---@package
function GithubRestProvider:fetch_workflows()
local Mapper = require('pipeline.providers.github.rest._mapper')

gh_api().get_workflows(self.server, self.repo, {
callback = function(workflows)
callback = function(err, workflows)
self.store.update_state(function(state)
state.pipelines = vim.tbl_map(Mapper.to_pipeline, workflows)
state.error = err and err.message or nil

if not state.error then
state.pipelines = vim.tbl_map(Mapper.to_pipeline, workflows)
end
end)
end,
})
end

---@package
function GithubRestProvider:fetch_runs()
local Mapper = require('pipeline.providers.github.rest._mapper')

gh_api().get_repository_workflow_runs(self.server, self.repo, 100, {
callback = function(workflow_runs)
callback = function(err, workflow_runs)
---@type pipeline.Run[]
local runs = vim.tbl_map(Mapper.to_run, workflow_runs)
local runs = vim.tbl_map(Mapper.to_run, workflow_runs or {})
---@type pipeline.Run[]
local old_runs = vim
.iter(vim.tbl_values(self.store.get_state().runs))
:flatten()
:totable()

self.store.update_state(function(state)
state.latest_run = runs[1]
state.runs = utils.group_by(function(run)
return run.pipeline_id
end, runs)
state.error = err and err.message or nil

if not state.error then
state.latest_run = runs[1]
state.runs = utils.group_by(function(run)
return run.pipeline_id
end, runs)
end
end)

local running_workflows = utils.uniq(
Expand All @@ -95,24 +114,35 @@ function GithubRestProvider:fetch()
)

for _, run in ipairs(running_workflows) do
gh_api().get_workflow_run_jobs(self.server, self.repo, run.run_id, 20, {
callback = function(jobs)
self.store.update_state(function(state)
state.jobs[run.run_id] = vim.tbl_map(Mapper.to_job, jobs)

for _, job in ipairs(jobs) do
state.steps[job.id] = vim.tbl_map(function(step)
return Mapper.to_step(job.id, step)
end, job.steps)
end
end)
end,
})
self:fetch_jobs(run.run_id)
end
end,
})
end

---@param run_id number
---@package
function GithubRestProvider:fetch_jobs(run_id)
local Mapper = require('pipeline.providers.github.rest._mapper')

gh_api().get_workflow_run_jobs(self.server, self.repo, run_id, 20, {
callback = function(err, jobs)
self.store.update_state(function(state)
state.error = err and err.message or nil
if not state.error then
state.jobs[run_id] = vim.tbl_map(Mapper.to_job, jobs)

for _, job in ipairs(jobs) do
state.steps[job.id] = vim.tbl_map(function(step)
return Mapper.to_step(job.id, step)
end, job.steps)
end
end
end)
end,
})
end

---@param pipeline pipeline.providers.github.rest.Pipeline|nil
function GithubRestProvider:dispatch(pipeline)
if not pipeline then
Expand Down Expand Up @@ -170,23 +200,26 @@ function GithubRestProvider:dispatch(pipeline)
pipeline.pipeline_id,
5,
{
callback = function(workflow_runs)
callback = function(err, workflow_runs)
local Mapper =
require('pipeline.providers.github.rest._mapper')
local runs = vim.tbl_map(Mapper.to_run, workflow_runs)

store.update_state(function(state)
state.runs = utils.group_by(
function(run)
return run.pipeline_id
end,
utils.uniq(function(run)
return run.run_id
end, {
unpack(runs),
unpack(vim.iter(state.runs):flatten():totable()),
})
)
state.error = err and err.message or nil
if not state.error then
state.runs = utils.group_by(
function(run)
return run.pipeline_id
end,
utils.uniq(function(run)
return run.run_id
end, {
unpack(runs),
unpack(vim.iter(state.runs):flatten():totable()),
})
)
end
end)
end,
}
Expand Down
Loading
Loading