Skip to content

Commit

Permalink
add code for API request logging #226
Browse files Browse the repository at this point in the history
  • Loading branch information
nelsonic committed Jan 27, 2025
1 parent a4b8b5f commit fd16b49
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 11 deletions.
138 changes: 138 additions & 0 deletions BUILDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ where (_hopefully_) it will all be clear.
- [2.2.3 Re-run the Tests](#223-re-run-the-tests)
- [3. Setup `GitHub` API](#3-setup-github-api)
- [3.1 Make the `user` Tests Pass](#31-make-the-user-tests-pass)
- [4. Log All `GitHub` API Request](#4-log-all-github-api-request)
- [X. Add Authentication](#x-add-authentication)
- [X.1 Add `auth_plug` to `deps`](#x1-add-auth_plug-to-deps)
- [X.2 Get your `AUTH_API_KEY`](#x2-get-your-auth_api_key)
Expand Down Expand Up @@ -761,6 +762,143 @@ They should now pass.
> if you don't please see:
> [dwyl/who#get-your-github-personal-access-token](https://github.com/dwyl/who?tab=readme-ov-file#get-your-github-personal-access-token)
# 4. Log All `GitHub` API Request

As noted in
[who#226](https://github.com/dwyl/who/issues/226)
the `GitHub` API is rate-limited to
`5,000 requests per hour`.
We would immediately exhaust this limit in a minute
and be **blocked** with a
[`429 Error`](https://www.rfc-editor.org/rfc/rfc6585#section-4) 🚫
if we make all the requests we need in one go.
So instead we need to _log_ all the requests
so that we know not to exceed the `5k/h` limit.

Create the `Request Log` (`Reqlog`) schema with the following command:

```sh
mix phx.gen.schema Reqlog reqlogs req:string param:string
```

The output is:

```sh
* creating lib/app/reqlog.ex
* creating priv/repo/migrations/20250127180724_create_reqlogs.exs
```

But for whatever reason it doesn't automatically the test file.
Create it manually with:

```sh
vi test/app/reqlog_test.exs
```

And _paste_ the following code:

```elixir
defmodule App.ReqlogTest do
use App.DataCase

test "App.Reqlog.create/1" do
owner = "dwyl"
reponame = "mvp"
record = %{
created_at: "2014-03-02T13:20:04Z",
req: "repository",
param: "#{owner}/#{reponame}"
}
assert {:ok, inserted} = App.Reqlog.create(record)
assert inserted.req == record.req
end

test "App.Reqlog.log/2" do
owner = "dwyl"
reponame = "mvp"

assert {:ok, inserted} = App.Reqlog.log("repo", "#{owner}/#{reponame}")
assert inserted.req == "repo"
assert inserted.param == "#{owner}/#{reponame}"
end
end
```

Running the test will fail:

```sh
mix test test/app/reqlog_test.exs
```

Open the `lib/app/reqlog.ex` file
and add the following code:

```elixir
@doc """
Creates a `reqlog` (request log) record.
"""
def create(attrs) do
%Reqlog{}
|> changeset(attrs)
|> Repo.insert()
end

def log(req, param) do
Logger.info "Fetching #{req} #{param}"
create(%{req: req, param: param})
end
```

At the top of the file under the `defmodule` add these two lines:

```sh
alias App.{Repo}
alias __MODULE__
require Logger
```

One allows us to use `Repo.insert/1`
`alias __MODULE__` defines an alias for our Elixir module.
`require Logger` loads the [`Logger`](https://hexdocs.pm/logger).

Ref:
https://alphahydrae.com/2021/03/how-to-make-an-elixir-module-alias-itself/

With that code in-place, we can _use_ it in our `GitHub` file.

Remember to add the line:

```elixir
use App.DataCase
```

to the top of the file `test/app/github_test.exs`
to ensure that the `GitHub` API requests get logged during testing.

In the `lib/app/github.ex` file,
add the following line to the top:

```elixir
import App.Reqlog, only: [log: 2]
```

Then replace the line:

```elixir
Logger.info "Fetching repository #{owner}/#{reponame}"
```

with:

```elixir
log("repository", "#{owner}/#{reponame}")
```

Replace any other instances of `Logger.info` with `log/2`.




# X. Add Authentication

This section borrows heavily from:
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

![who-banner](https://user-images.githubusercontent.com/194400/194710724-0e2de0b1-0b2a-4ee8-83a0-eb07cce74810.png)

The **quick _answer_**
The **quick _answer_**
to the question:
**_Who_ is in the `@dwyl` community?**

Expand All @@ -18,6 +18,7 @@ to the question:

# **`TODO`**: re-generate the "wall of faces" using latest data `#HelpWanted`


![face wall](https://user-images.githubusercontent.com/194400/28011265-a95f52d4-6559-11e7-823e-6133d947921a.jpg)

# *Why*?
Expand Down Expand Up @@ -52,9 +53,9 @@ contributing to the

Visit
[github.com/orgs/dwyl/people](https://github.com/orgs/dwyl/people)
you can see a list of people
who are _members_ of the Org.
*Simple. effective. incomplete*.
you can see a list of people
who are _members_ of the Org.
*Simple. effective. incomplete*.
This list only scratches the surface!

## 2. List all contributors to dwyl repos on `GitHub`
Expand Down Expand Up @@ -106,7 +107,8 @@ in your terminal:
git clone git@github.com:dwyl/who.git && cd who
mix setup
```
That will download the **`code`**,

That will download the **`code`**,
install dependencies
and create the necessary database + tables.

Expand Down
17 changes: 11 additions & 6 deletions lib/app/github.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule App.GitHub do
Handles all interactions with the GitHub REST API
via: github.com/edgurgel/tentacat Elixir GitHub Lib.
"""
require Logger
import App.Reqlog, only: [log: 2]

@access_token Application.compile_env(:tentacat, :access_token)
@client Tentacat.Client.new(%{access_token: @access_token})
Expand All @@ -13,7 +13,8 @@ defmodule App.GitHub do
Returns the GitHub repository data.
"""
def repository(owner, reponame) do
Logger.info "Fetching repository #{owner}/#{reponame}"
log("repository", "#{owner}/#{reponame}")
# Logger.info "Fetching repository #{owner}/#{reponame}"
{_status, data, _res} =
Tentacat.Repositories.repo_get(@client, owner, reponame)
data
Expand All @@ -23,7 +24,8 @@ defmodule App.GitHub do
Returns the list of GitHub repositories for an Org.
"""
def org_repos(owner) do
Logger.info "Fetching list of repositories for #{owner}"
log("org_repos", owner)
# Logger.info "Fetching list of repositories for #{owner}"
{_status, data, _res} =
Tentacat.Repositories.list_orgs(@client, owner)
data
Expand All @@ -33,7 +35,8 @@ defmodule App.GitHub do
`user/1` Returns the GitHub user profile data.
"""
def user(username) do
Logger.info "Fetching user #{username}"
# Logger.info "Fetching user #{username}"
log("user", username)
{_status, data, _res} = Tentacat.Users.find(@client, username)
data
end
Expand All @@ -42,7 +45,8 @@ defmodule App.GitHub do
`org_user_list/1` Returns the list of GitHub users for an org.
"""
def org_user_list(orgname) do
Logger.info "Fetching org user list for #{orgname}"
# Logger.info "Fetching org user list for #{orgname}"
log("org_user_list", orgname)
{_status, data, _res} =
Tentacat.Organizations.Members.list(@client, orgname)
data
Expand All @@ -54,7 +58,8 @@ defmodule App.GitHub do
`repo` - name of the repo to check stargazers for.
"""
def repo_stargazers(owner, repo) do
Logger.info "Fetching stargazers for #{owner}/#{repo}"
log("repo_stargazers", "#{owner}/#{repo}")
# Logger.info "Fetching stargazers for #{owner}/#{repo}"
{_status, data, _res} =
Tentacat.Users.Starring.stargazers(@client, owner, repo)
data
Expand Down
37 changes: 37 additions & 0 deletions lib/app/reqlog.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule App.Reqlog do
use Ecto.Schema
import Ecto.Changeset
alias App.{Repo}
alias __MODULE__
require Logger

schema "reqlogs" do
field :req, :string
field :param, :string

timestamps()
end

@doc false
def changeset(reqlog, attrs) do
reqlog
|> cast(attrs, [:req, :param])
|> validate_required([:req, :param])
end

@doc """
Creates a `reqlog` (request log) record.
"""
def create(attrs) do
%Reqlog{}
|> changeset(attrs)
|> Repo.insert()
end

# clean interface function that can be called from App.GitHub functions
# e.g: log("repository", "#{owner}/#{reponame}")
def log(req, param) do
Logger.info "Fetching #{req} #{param}"
create(%{req: req, param: param})
end
end
12 changes: 12 additions & 0 deletions priv/repo/migrations/20250127180724_create_reqlogs.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule App.Repo.Migrations.CreateReqlogs do
use Ecto.Migration

def change do
create table(:reqlogs) do
add :req, :string
add :param, :string

timestamps()
end
end
end
1 change: 1 addition & 0 deletions test/app/github_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule App.GitHubTest do
use ExUnit.Case
use App.DataCase
alias App.GitHub

test "App.GitHub.repository/1" do
Expand Down
24 changes: 24 additions & 0 deletions test/app/reqlog_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule App.ReqlogTest do
use App.DataCase

test "App.Reqlog.create/1" do
owner = "dwyl"
reponame = "mvp"
record = %{
created_at: "2014-03-02T13:20:04Z",
req: "repository",
param: "#{owner}/#{reponame}"
}
assert {:ok, inserted} = App.Reqlog.create(record)
assert inserted.req == record.req
end

test "App.Reqlog.log/2" do
owner = "dwyl"
reponame = "mvp"

assert {:ok, inserted} = App.Reqlog.log("repo", "#{owner}/#{reponame}")
assert inserted.req == "repo"
assert inserted.param == "#{owner}/#{reponame}"
end
end

0 comments on commit fd16b49

Please sign in to comment.