Skip to content

Commit

Permalink
feat(registry): add api module (#524)
Browse files Browse the repository at this point in the history
  • Loading branch information
williamboman committed Oct 8, 2022
1 parent d726743 commit 72efedb
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 25 deletions.
41 changes: 16 additions & 25 deletions lua/mason-core/managers/github/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local _ = require "mason-core.functional"
local log = require "mason-core.log"
local fetch = require "mason-core.fetch"
local spawn = require "mason-core.spawn"
local api = require "mason-registry.api"

local M = {}

Expand All @@ -10,12 +11,14 @@ local M = {}
---@alias GitHubTag {name: string}
---@alias GitHubCommit {sha: string}

local stringify_params = _.compose(_.join "&", _.map(_.join "="), _.sort_by(_.head), _.to_pairs)

---@param path string
---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
local function api_call(path, opts)
local function gh_api_call(path, opts)
if opts and opts.params then
local params = _.join("&", _.map(_.join "=", _.sort_by(_.head, _.to_pairs(opts.params))))
local params = stringify_params(opts.params)
path = ("%s?%s"):format(path, params)
end
return spawn
Expand All @@ -27,27 +30,15 @@ local function api_call(path, opts)
:map_catching(vim.json.decode)
end

M.api_call = api_call

---@param path string
---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
local function proxy_api_call(path, opts)
if opts and opts.params then
local params = _.join("&", _.map(_.join "=", _.sort_by(_.head, _.to_pairs(opts.params))))
path = ("%s?%s"):format(path, params)
end
-- https://github.com/williamboman/github-api-proxy
return fetch(("https://github-api-proxy.redwill.se/%s"):format(path))
end
M.api_call = gh_api_call

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubRelease[]>
function M.fetch_releases(repo)
log.fmt_trace("Fetching GitHub releases for repo=%s", repo)
local path = ("repos/%s/releases"):format(repo)
return api_call(path):map_err(function()
return gh_api_call(path):map_err(function()
return ("Failed to fetch releases for GitHub repository %s."):format(repo)
end)
end
Expand All @@ -58,7 +49,7 @@ end
function M.fetch_release(repo, tag_name)
log.fmt_trace("Fetching GitHub release for repo=%s, tag_name=%s", repo, tag_name)
local path = ("repos/%s/releases/tags/%s"):format(repo, tag_name)
return api_call(path):map_err(function()
return gh_api_call(path):map_err(function()
return ("Failed to fetch release %q for GitHub repository %s."):format(tag_name, repo)
end)
end
Expand All @@ -71,20 +62,20 @@ end
---@return Result # Result<GitHubRelease>
function M.fetch_latest_release(repo, opts)
opts = opts or { include_prerelease = false }
local path = ("api/repo/%s/latest-release"):format(repo)
return proxy_api_call(path, {
local path = ("/api/repo/%s/latest-release"):format(repo)
return api.get(path, {
params = {
include_prerelease = opts.include_prerelease and "true" or "false",
},
}):map_catching(vim.json.decode)
})
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubTag[]>
function M.fetch_tags(repo)
local path = ("repos/%s/tags"):format(repo)
return api_call(path):map_err(function()
return gh_api_call(path):map_err(function()
return ("Failed to fetch tags for GitHub repository %s."):format(repo)
end)
end
Expand All @@ -93,8 +84,8 @@ end
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<string> The latest tag name.
function M.fetch_latest_tag(repo)
local path = ("api/repo/%s/latest-tag"):format(repo)
return proxy_api_call(path):map_catching(vim.json.decode):map(_.prop "tag")
local path = ("/api/repo/%s/latest-tag"):format(repo)
return api.get(path):map(_.prop "tag")
end

---@async
Expand All @@ -103,7 +94,7 @@ end
---@return Result # Result<GitHubCommit[]>
function M.fetch_commits(repo, opts)
local path = ("repos/%s/commits"):format(repo)
return api_call(path, {
return gh_api_call(path, {
params = {
page = opts and opts.page or 1,
per_page = opts and opts.per_page or 30,
Expand All @@ -119,7 +110,7 @@ end
---@async
--@return Result @of GitHubRateLimitResponse
function M.fetch_rate_limit()
return api_call "rate_limit"
return gh_api_call "rate_limit"
end

return M
21 changes: 21 additions & 0 deletions lua/mason-registry/api.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
local _ = require "mason-core.functional"
local fetch = require "mason-core.fetch"

local api = {}

local stringify_params = _.compose(_.join "&", _.map(_.join "="), _.sort_by(_.head), _.to_pairs)

---@async
---@param path string
---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
function api.get(path, opts)
if opts and opts.params then
local params = stringify_params(opts.params)
path = ("%s?%s"):format(path, params)
end
-- https://github.com/williamboman/mason-registry-api
return fetch(("https://api.mason-registry.dev%s"):format(path)):map_catching(vim.json.decode)
end

return api
37 changes: 37 additions & 0 deletions tests/mason-registry/api_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
local stub = require "luassert.stub"
local Result = require "mason-core.result"

describe("mason-registry API", function()
---@module "mason-registry.api"
local api
local fetch
before_each(function()
fetch = stub.new()
package.loaded["mason-core.fetch"] = fetch
package.loaded["mason-registry.api"] = nil
api = require "mason-registry.api"
end)

it("should stringify query parameters", function()
fetch.returns(Result.success [[{}]])

api.get("/api/data", {
params = {
page = 2,
page_limit = 10,
sort = "ASC",
},
})

assert.spy(fetch).was_called(1)
assert.spy(fetch).was_called_with "https://api.mason-registry.dev/api/data?page=2&page_limit=10&sort=ASC"
end)

it("should deserialize JSON", function()
fetch.returns(Result.success [[{"field": ["value"]}]])

local result = api.get("/"):get_or_throw()

assert.same({ field = { "value" } }, result)
end)
end)

0 comments on commit 72efedb

Please sign in to comment.