generated from kommitters/.template
-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add github fetcher * Add test to the github fetcher * Add a mapper for Github * Add test for the github mapper * Fix rubocop warnings * Fix typo
- Loading branch information
1 parent
7daff86
commit f8788bc
Showing
9 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
module Domain | ||
## | ||
# The Domain::Issue class provides a domain-specific representation of a Github issue object. | ||
# It encapsulates information about a repository issue, including the title, state, assignees, | ||
# description, and the repository url. | ||
# | ||
class Issue | ||
attr_reader :title, :state, :assignees, :description, :url | ||
|
||
ATTRIBUTES = %w[title state assignees description url].freeze | ||
|
||
def initialize(title, state, assignees, body, url) | ||
@title = title | ||
@state = state | ||
@assignees = assignees | ||
@description = body | ||
@url = url | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# frozen_string_literal: true | ||
|
||
require "octokit" | ||
require "openssl" | ||
require "jwt" | ||
|
||
require_relative "../base" | ||
require_relative "./types/response" | ||
|
||
module Fetcher | ||
module Github | ||
## | ||
# This class is an implementation of the Fetcher::Base interface, specifically designed | ||
# for fetching data from a GitHub repository. | ||
# | ||
class Base < Fetcher::Base | ||
protected | ||
|
||
# Implements the data fetching logic to get data from a Github repository. | ||
# It connects to Github using the octokit gem, authenticates with a github app, | ||
# request the data and returns a validated response. | ||
# | ||
def execute(method, *filter) | ||
octokit_response = octokit.public_send(method, *filter) | ||
|
||
Fetcher::Github::Types::Response.new(octokit_response) | ||
end | ||
|
||
private | ||
|
||
def octokit | ||
Octokit::Client.new(bearer_token: access_token) | ||
end | ||
|
||
def access_token | ||
app = Octokit::Client.new(client_id: config[:app_id], bearer_token: jwt) | ||
|
||
app.create_app_installation_access_token(config[:installation_id])[:token] | ||
end | ||
|
||
def jwt | ||
private_pem = File.read(config[:secret_path]) | ||
private_key = OpenSSL::PKey::RSA.new(private_pem) | ||
|
||
JWT.encode(jwt_payload, private_key, "RS256") | ||
end | ||
|
||
def jwt_payload | ||
{ | ||
iat: Time.now.to_i - 60, | ||
exp: Time.now.to_i + (10 * 60), | ||
iss: config[:app_id] | ||
} | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# frozen_string_literal: true | ||
|
||
module Fetcher | ||
module Github | ||
module Types | ||
## | ||
# Represents a response received from the Octokit Github client. It encapsulates essential | ||
# information about the response, providing a structured way to handle and analyze | ||
# it's responses. | ||
class Response | ||
attr_reader :status_code, :message, :results | ||
|
||
def initialize(response) | ||
if response.empty? | ||
@status_code = 404 | ||
@message = "no result were found" | ||
@results = [] | ||
else | ||
@status_code = 200 | ||
@message = "success" | ||
@results = response | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "../base" | ||
|
||
module Fetcher | ||
module Github | ||
## | ||
# This class is an implementation of the Fetcher::Github::Base interface, specifically designed | ||
# for fetching issues from a Github repository. | ||
# | ||
class RepoIssues < Github::Base | ||
def fetch | ||
execute("list_issues", config[:repo]) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "../../domain/issue" | ||
require_relative "../base" | ||
|
||
module Mapper | ||
module Github | ||
## | ||
# This class implementats the methods of the Mapper::Base module, specifically designed for | ||
# preparing or shaping Github issues data coming from a Fetcher::Base implementation. | ||
class Issues | ||
include Base | ||
|
||
# Implements the logic for shaping the results from a fetcher response. | ||
# | ||
# <br> | ||
# <b>Params:</b> | ||
# * <tt>Fetcher::Github::Types::Response</tt> github_response: Array of github issues data. | ||
# | ||
# <br> | ||
# <b>return</b> <tt>List<Domain::Issue></tt> mapped github issues to be used by a | ||
# Formatter::Base implementation. | ||
# | ||
def map(github_response) | ||
return [] if github_response.results.empty? | ||
|
||
normalized_github_data = normalize_response(github_response.results) | ||
|
||
normalized_github_data.map do |issue| | ||
Domain::Issue.new( | ||
issue["title"], issue["state"], issue["assignees"], issue["body"], issue["url"] | ||
) | ||
end | ||
end | ||
|
||
private | ||
|
||
def normalize_response(results) | ||
return [] if results.nil? | ||
|
||
results.map do |value| | ||
{ | ||
"title" => value[:title], | ||
"state" => value[:state], | ||
"assignees" => extract_assignees(value[:assignees]), | ||
"body" => value[:body], | ||
"url" => value[:url] | ||
} | ||
end | ||
end | ||
|
||
def extract_assignees(assignees) | ||
assignees.map { |assignee| assignee[:login] } | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Fetcher::Github::RepoIssues do | ||
before do | ||
config = { | ||
app_id: "123456", | ||
installation_id: "78910", | ||
secret_path: "secrets_file_path.pem", | ||
repo: "Organization/Repository" | ||
} | ||
|
||
@fetcher = described_class.new(config) | ||
end | ||
|
||
describe "attributes and arguments" do | ||
it { expect(described_class).to respond_to(:new).with(1).arguments } | ||
|
||
it { expect(@fetcher).to respond_to(:config) } | ||
it { expect(@fetcher).to respond_to(:fetch).with(0).arguments } | ||
end | ||
|
||
describe ".fetch" do | ||
let(:empty_response) { [] } | ||
let(:response) { [{ url: "repo_url" }] } | ||
|
||
let(:octokit) do | ||
stub = { | ||
create_app_installation_access_token: { token: "access_token" } | ||
} | ||
|
||
instance_double(Octokit::Client, stub) | ||
end | ||
|
||
before do | ||
allow(File).to receive(:read).and_return("private_pem") | ||
allow(OpenSSL::PKey::RSA).to receive(:new).and_return("private_key") | ||
allow(JWT).to receive(:encode).and_return("jwt_token") | ||
allow(Octokit::Client).to receive(:new).and_return(octokit) | ||
end | ||
|
||
it "fetch issues from the Github repo when there are no 'issues'" do | ||
allow(octokit).to receive(:public_send).and_return(empty_response) | ||
|
||
fetched_data = @fetcher.fetch | ||
|
||
expect(fetched_data).to be_an_instance_of(Fetcher::Github::Types::Response) | ||
expect(fetched_data.results).to be_an_instance_of(Array) | ||
expect(fetched_data.results.length).to eq(0) | ||
end | ||
|
||
it "fetch issues from the Github repo when there are 'issues'" do | ||
allow(octokit).to receive(:public_send).and_return(response) | ||
|
||
fetched_data = @fetcher.fetch | ||
|
||
expect(fetched_data).to be_an_instance_of(Fetcher::Github::Types::Response) | ||
expect(fetched_data.results).to be_an_instance_of(Array) | ||
expect(fetched_data.results.length).to eq(1) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Mapper::Github::Issues do | ||
let(:assignees) { [{ login: "username" }] } | ||
let(:issues) do | ||
[{ | ||
url: "repo_url", | ||
title: "title", | ||
state: "state", | ||
assignees: assignees, | ||
description: "description" | ||
}] | ||
end | ||
|
||
before do | ||
@imap_response = Fetcher::Github::Types::Response.new(issues) | ||
@mapper = described_class.new | ||
end | ||
|
||
describe "attributes and arguments" do | ||
it { expect(described_class).to respond_to(:new).with(0).arguments } | ||
it { expect(@mapper).to respond_to(:map).with(1).arguments } | ||
end | ||
|
||
describe ".map" do | ||
it "maps the given data into an array of Domain::Issue instances" do | ||
mapped_data = @mapper.map(@imap_response) | ||
|
||
are_issues = mapped_data.all? { |element| element.is_a?(Domain::Issue) } | ||
|
||
expect(mapped_data).to be_an_instance_of(Array) | ||
expect(mapped_data.length).to eq(1) | ||
expect(are_issues).to be_truthy | ||
end | ||
end | ||
end |