bas 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile +6 -0
- data/README.md +2 -2
- data/lib/bas/domain/issue.rb +22 -0
- data/lib/bas/domain/pto.rb +45 -4
- data/lib/bas/fetcher/github/base.rb +57 -0
- data/lib/bas/fetcher/github/types/response.rb +27 -0
- data/lib/bas/fetcher/github/use_case/repo_issues.rb +17 -0
- data/lib/bas/formatter/pto.rb +19 -22
- data/lib/bas/mapper/github/issues.rb +57 -0
- data/lib/bas/mapper/notion/pto_today.rb +17 -14
- data/lib/bas/mapper/postgres/pto_today.rb +2 -2
- data/lib/bas/use_cases/use_cases.rb +2 -0
- data/lib/bas/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97cc25416cff1a50cad38e85bbc1e7389693ccf2f4004a1c5fb8d5049162b407
|
4
|
+
data.tar.gz: efef410e0d776c8808655dbd20cb7500c81bcf53c16f072a7103d5827def9e58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7d915c41461667d858750e117e63468fba9cdea4fac59dfd80b0dcf1a7c4ce749b773378b06a81e2cdfafbefce92a61a2ee8415a80edd6a1e1a61380c5bdda9
|
7
|
+
data.tar.gz: fa174b7e86b013055efabe32b7dc7c2f213bd8dd18600c35e655d4c7512ce8e8f7ed4e104c091ca5b4def7180d4499a1c37f3dd5f4ad5e0a936ac79d716f03b1
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.2.0 (04.04.2024)
|
4
|
+
- [Add a GitHub fetcher component](https://github.com/kommitters/bas/issues/6)
|
5
|
+
- [Allow daily hours pto for more than one day pto](https://github.com/kommitters/bas/issues/8)
|
6
|
+
|
3
7
|
## 0.1.0 (26.03.2024)
|
4
8
|
- [Add the BNS gem code](https://github.com/kommitters/bas/issues/1)
|
data/Gemfile
CHANGED
@@ -5,10 +5,16 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in bas.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem "jwt", "~> 2.8.1"
|
9
|
+
|
8
10
|
gem "rake", "~> 13.0"
|
9
11
|
|
10
12
|
gem "net-imap", "~> 0.4.10"
|
11
13
|
gem "net-smtp", "~> 0.4.0.1"
|
14
|
+
|
15
|
+
gem "octokit", "~> 8.1.0"
|
16
|
+
gem "openssl", "~> 3.2"
|
17
|
+
|
12
18
|
gem "rspec", "~> 3.0"
|
13
19
|
gem "rubocop", "~> 1.21"
|
14
20
|
gem "simplecov", require: false, group: :test
|
data/README.md
CHANGED
@@ -6,11 +6,11 @@ The underlying idea is to develop generic components that can serve a wide range
|
|
6
6
|
|
7
7
|

|
8
8
|

|
9
|
-

|
10
10
|
[](https://coveralls.io/github/kommitters/bas?branch=main)
|
11
11
|

|
12
12
|
[](https://api.securityscorecards.dev/projects/github.com/kommitters/bas)
|
13
|
-
[](https://bestpractices.coreinfrastructure.org/projects/8713)
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
##
|
5
|
+
# The Domain::Issue class provides a domain-specific representation of a Github issue object.
|
6
|
+
# It encapsulates information about a repository issue, including the title, state, assignees,
|
7
|
+
# description, and the repository url.
|
8
|
+
#
|
9
|
+
class Issue
|
10
|
+
attr_reader :title, :state, :assignees, :description, :url
|
11
|
+
|
12
|
+
ATTRIBUTES = %w[title state assignees description url].freeze
|
13
|
+
|
14
|
+
def initialize(title, state, assignees, body, url)
|
15
|
+
@title = title
|
16
|
+
@state = state
|
17
|
+
@assignees = assignees
|
18
|
+
@description = body
|
19
|
+
@url = url
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/bas/domain/pto.rb
CHANGED
@@ -7,9 +7,9 @@ module Domain
|
|
7
7
|
# the start date, and the end date of the time off period.
|
8
8
|
#
|
9
9
|
class Pto
|
10
|
-
attr_reader :individual_name, :
|
10
|
+
attr_reader :individual_name, :start_date_from, :start_date_to, :end_date_from, :end_date_to
|
11
11
|
|
12
|
-
ATTRIBUTES = %w[individual_name
|
12
|
+
ATTRIBUTES = %w[individual_name start_date_from start_date_to end_date_from end_date_to].freeze
|
13
13
|
|
14
14
|
# Initializes a Domain::Pto instance with the specified individual name, start date, and end date.
|
15
15
|
#
|
@@ -21,8 +21,49 @@ module Domain
|
|
21
21
|
#
|
22
22
|
def initialize(individual_name, start_date, end_date)
|
23
23
|
@individual_name = individual_name
|
24
|
-
|
25
|
-
@
|
24
|
+
|
25
|
+
@start_date_from = start_date[:from]
|
26
|
+
@start_date_to = start_date[:to]
|
27
|
+
@end_date_from = end_date[:from]
|
28
|
+
@end_date_to = end_date[:to]
|
29
|
+
end
|
30
|
+
|
31
|
+
def same_day?
|
32
|
+
start_date = extract_date(start_date_from)
|
33
|
+
end_date = extract_date(end_date_from)
|
34
|
+
|
35
|
+
start_date == end_date
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_timezone(timezone)
|
39
|
+
@start_date_from = set_timezone(start_date_from, timezone)
|
40
|
+
@start_date_to = set_timezone(start_date_to, timezone)
|
41
|
+
@end_date_from = set_timezone(end_date_from, timezone)
|
42
|
+
@end_date_to = set_timezone(end_date_to, timezone)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def extract_date(date)
|
48
|
+
return if date.nil?
|
49
|
+
|
50
|
+
date.strftime("%F")
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_date_time(date, timezone)
|
54
|
+
return if date.nil?
|
55
|
+
|
56
|
+
date_time = date.include?("T") ? date : "#{date}T00:00:00.000#{timezone}"
|
57
|
+
|
58
|
+
DateTime.parse(date_time).to_time
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_timezone(date, timezone)
|
62
|
+
return if date.nil?
|
63
|
+
|
64
|
+
date_time = build_date_time(date, timezone)
|
65
|
+
|
66
|
+
Time.at(date_time, in: timezone)
|
26
67
|
end
|
27
68
|
end
|
28
69
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "octokit"
|
4
|
+
require "openssl"
|
5
|
+
require "jwt"
|
6
|
+
|
7
|
+
require_relative "../base"
|
8
|
+
require_relative "./types/response"
|
9
|
+
|
10
|
+
module Fetcher
|
11
|
+
module Github
|
12
|
+
##
|
13
|
+
# This class is an implementation of the Fetcher::Base interface, specifically designed
|
14
|
+
# for fetching data from a GitHub repository.
|
15
|
+
#
|
16
|
+
class Base < Fetcher::Base
|
17
|
+
protected
|
18
|
+
|
19
|
+
# Implements the data fetching logic to get data from a Github repository.
|
20
|
+
# It connects to Github using the octokit gem, authenticates with a github app,
|
21
|
+
# request the data and returns a validated response.
|
22
|
+
#
|
23
|
+
def execute(method, *filter)
|
24
|
+
octokit_response = octokit.public_send(method, *filter)
|
25
|
+
|
26
|
+
Fetcher::Github::Types::Response.new(octokit_response)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def octokit
|
32
|
+
Octokit::Client.new(bearer_token: access_token)
|
33
|
+
end
|
34
|
+
|
35
|
+
def access_token
|
36
|
+
app = Octokit::Client.new(client_id: config[:app_id], bearer_token: jwt)
|
37
|
+
|
38
|
+
app.create_app_installation_access_token(config[:installation_id])[:token]
|
39
|
+
end
|
40
|
+
|
41
|
+
def jwt
|
42
|
+
private_pem = File.read(config[:secret_path])
|
43
|
+
private_key = OpenSSL::PKey::RSA.new(private_pem)
|
44
|
+
|
45
|
+
JWT.encode(jwt_payload, private_key, "RS256")
|
46
|
+
end
|
47
|
+
|
48
|
+
def jwt_payload
|
49
|
+
{
|
50
|
+
iat: Time.now.to_i - 60,
|
51
|
+
exp: Time.now.to_i + (10 * 60),
|
52
|
+
iss: config[:app_id]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
module Github
|
5
|
+
module Types
|
6
|
+
##
|
7
|
+
# Represents a response received from the Octokit Github client. It encapsulates essential
|
8
|
+
# information about the response, providing a structured way to handle and analyze
|
9
|
+
# it's responses.
|
10
|
+
class Response
|
11
|
+
attr_reader :status_code, :message, :results
|
12
|
+
|
13
|
+
def initialize(response)
|
14
|
+
if response.empty?
|
15
|
+
@status_code = 404
|
16
|
+
@message = "no result were found"
|
17
|
+
@results = []
|
18
|
+
else
|
19
|
+
@status_code = 200
|
20
|
+
@message = "success"
|
21
|
+
@results = response
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
module Github
|
7
|
+
##
|
8
|
+
# This class is an implementation of the Fetcher::Github::Base interface, specifically designed
|
9
|
+
# for fetching issues from a Github repository.
|
10
|
+
#
|
11
|
+
class RepoIssues < Github::Base
|
12
|
+
def fetch
|
13
|
+
execute("list_issues", config[:repo])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/bas/formatter/pto.rb
CHANGED
@@ -38,6 +38,8 @@ module Formatter
|
|
38
38
|
def format(ptos_list)
|
39
39
|
raise Formatter::Exceptions::InvalidData unless ptos_list.all? { |pto| pto.is_a?(Domain::Pto) }
|
40
40
|
|
41
|
+
ptos_list.each { |pto| pto.format_timezone(@timezone) }
|
42
|
+
|
41
43
|
ptos_list.reduce("") do |payload, pto|
|
42
44
|
built_template = build_template(Domain::Pto::ATTRIBUTES, pto)
|
43
45
|
payload + format_message_by_case(built_template.gsub("\n", ""), pto)
|
@@ -47,42 +49,37 @@ module Formatter
|
|
47
49
|
private
|
48
50
|
|
49
51
|
def format_message_by_case(built_template, pto)
|
50
|
-
|
51
|
-
date_end = format_timezone(pto.end_date).strftime("%F")
|
52
|
-
|
53
|
-
if date_start == date_end
|
52
|
+
if pto.same_day?
|
54
53
|
interval = same_day_interval(pto)
|
55
|
-
day_message = today?(
|
54
|
+
day_message = today?(pto.start_date_from) ? "today" : "the day #{pto.start_date_from.strftime("%F")}"
|
56
55
|
|
57
56
|
"#{built_template} #{day_message} #{interval}\n"
|
58
57
|
else
|
59
|
-
|
58
|
+
start_date_interval = day_interval(pto.start_date_from, pto.start_date_to)
|
59
|
+
end_date_interval = day_interval(pto.end_date_from, pto.end_date_to)
|
60
|
+
|
61
|
+
"#{built_template} from #{start_date_interval} to #{end_date_interval}\n"
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
def
|
64
|
-
|
65
|
-
time_end = format_timezone(pto.end_date).strftime("%I:%M %P")
|
66
|
-
|
67
|
-
time_start == time_end ? "all day" : "from #{time_start} to #{time_end}"
|
68
|
-
end
|
65
|
+
def day_interval(start_date, end_date)
|
66
|
+
return start_date.strftime("%F") if end_date.nil?
|
69
67
|
|
70
|
-
|
71
|
-
|
68
|
+
time_start = start_date.strftime("%I:%M %P")
|
69
|
+
time_end = end_date.strftime("%I:%M %P")
|
72
70
|
|
73
|
-
|
71
|
+
"#{start_date.strftime("%F")} (#{time_start} - #{time_end})"
|
74
72
|
end
|
75
73
|
|
76
|
-
def
|
77
|
-
|
74
|
+
def same_day_interval(pto)
|
75
|
+
time_start = pto.start_date_from.strftime("%I:%M %P")
|
76
|
+
time_end = pto.end_date_from.strftime("%I:%M %P")
|
78
77
|
|
79
|
-
|
78
|
+
time_start == time_end ? "all day" : "from #{time_start} to #{time_end}"
|
80
79
|
end
|
81
80
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
DateTime.parse(date_time).to_time
|
81
|
+
def today?(date)
|
82
|
+
date == Time.now(in: @timezone).strftime("%F")
|
86
83
|
end
|
87
84
|
end
|
88
85
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/issue"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
module Github
|
8
|
+
##
|
9
|
+
# This class implementats the methods of the Mapper::Base module, specifically designed for
|
10
|
+
# preparing or shaping Github issues data coming from a Fetcher::Base implementation.
|
11
|
+
class Issues
|
12
|
+
include Base
|
13
|
+
|
14
|
+
# Implements the logic for shaping the results from a fetcher response.
|
15
|
+
#
|
16
|
+
# <br>
|
17
|
+
# <b>Params:</b>
|
18
|
+
# * <tt>Fetcher::Github::Types::Response</tt> github_response: Array of github issues data.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>return</b> <tt>List<Domain::Issue></tt> mapped github issues to be used by a
|
22
|
+
# Formatter::Base implementation.
|
23
|
+
#
|
24
|
+
def map(github_response)
|
25
|
+
return [] if github_response.results.empty?
|
26
|
+
|
27
|
+
normalized_github_data = normalize_response(github_response.results)
|
28
|
+
|
29
|
+
normalized_github_data.map do |issue|
|
30
|
+
Domain::Issue.new(
|
31
|
+
issue["title"], issue["state"], issue["assignees"], issue["body"], issue["url"]
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def normalize_response(results)
|
39
|
+
return [] if results.nil?
|
40
|
+
|
41
|
+
results.map do |value|
|
42
|
+
{
|
43
|
+
"title" => value[:title],
|
44
|
+
"state" => value[:state],
|
45
|
+
"assignees" => extract_assignees(value[:assignees]),
|
46
|
+
"body" => value[:body],
|
47
|
+
"url" => value[:url]
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def extract_assignees(assignees)
|
53
|
+
assignees.map { |assignee| assignee[:login] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -42,19 +42,11 @@ module Mapper
|
|
42
42
|
response.map do |value|
|
43
43
|
pto_fields = value["properties"].slice(*PTO_PARAMS)
|
44
44
|
|
45
|
-
|
46
|
-
pto_fields[
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def extract_pto_value(field, value)
|
54
|
-
case field
|
55
|
-
when "Person" then extract_person_field_value(value)
|
56
|
-
when "Desde?" then extract_date_field_value(value)
|
57
|
-
when "Hasta?" then extract_date_field_value(value)
|
45
|
+
{
|
46
|
+
"Person" => extract_person_field_value(pto_fields["Person"]),
|
47
|
+
"Desde?" => extract_date_field_value(pto_fields["Desde?"]),
|
48
|
+
"Hasta?" => extract_date_field_value(pto_fields["Hasta?"])
|
49
|
+
}
|
58
50
|
end
|
59
51
|
end
|
60
52
|
|
@@ -62,9 +54,20 @@ module Mapper
|
|
62
54
|
data["people"][0]["name"]
|
63
55
|
end
|
64
56
|
|
65
|
-
def extract_date_field_value(
|
57
|
+
def extract_date_field_value(date)
|
58
|
+
{
|
59
|
+
from: extract_start_date(date),
|
60
|
+
to: extract_end_date(date)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_start_date(data)
|
66
65
|
data["date"]["start"]
|
67
66
|
end
|
67
|
+
|
68
|
+
def extract_end_date(data)
|
69
|
+
data["date"]["end"]
|
70
|
+
end
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
@@ -27,8 +27,8 @@ module Mapper
|
|
27
27
|
|
28
28
|
ptos.map do |pto|
|
29
29
|
name = pto["name"]
|
30
|
-
start_date = pto["start_date"]
|
31
|
-
end_date = pto["end_date"]
|
30
|
+
start_date = { from: pto["start_date"], to: nil }
|
31
|
+
end_date = { from: pto["end_date"], to: nil }
|
32
32
|
|
33
33
|
Domain::Pto.new(name, start_date, end_date)
|
34
34
|
end
|
@@ -8,6 +8,7 @@ require_relative "../fetcher/notion/use_case/pto_next_week"
|
|
8
8
|
require_relative "../fetcher/notion/use_case/work_items_limit"
|
9
9
|
require_relative "../fetcher/postgres/use_case/pto_today"
|
10
10
|
require_relative "../fetcher/imap/use_case/support_emails"
|
11
|
+
require_relative "../fetcher/github/use_case/repo_issues"
|
11
12
|
|
12
13
|
# mapper
|
13
14
|
require_relative "../mapper/notion/birthday_today"
|
@@ -15,6 +16,7 @@ require_relative "../mapper/notion/pto_today"
|
|
15
16
|
require_relative "../mapper/notion/work_items_limit"
|
16
17
|
require_relative "../mapper/postgres/pto_today"
|
17
18
|
require_relative "../mapper/imap/support_emails"
|
19
|
+
require_relative "../mapper/github/issues"
|
18
20
|
|
19
21
|
# formatter
|
20
22
|
require_relative "../formatter/birthday"
|
data/lib/bas/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kommitters Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A versatile business automation system offering key components for building
|
14
14
|
various use cases. It provides an easy-to-use tool for implementing automation
|
@@ -41,9 +41,13 @@ files:
|
|
41
41
|
- lib/bas/domain/birthday.rb
|
42
42
|
- lib/bas/domain/email.rb
|
43
43
|
- lib/bas/domain/exceptions/function_not_implemented.rb
|
44
|
+
- lib/bas/domain/issue.rb
|
44
45
|
- lib/bas/domain/pto.rb
|
45
46
|
- lib/bas/domain/work_items_limit.rb
|
46
47
|
- lib/bas/fetcher/base.rb
|
48
|
+
- lib/bas/fetcher/github/base.rb
|
49
|
+
- lib/bas/fetcher/github/types/response.rb
|
50
|
+
- lib/bas/fetcher/github/use_case/repo_issues.rb
|
47
51
|
- lib/bas/fetcher/imap/base.rb
|
48
52
|
- lib/bas/fetcher/imap/types/response.rb
|
49
53
|
- lib/bas/fetcher/imap/use_case/support_emails.rb
|
@@ -68,6 +72,7 @@ files:
|
|
68
72
|
- lib/bas/formatter/support_emails.rb
|
69
73
|
- lib/bas/formatter/work_items_limit.rb
|
70
74
|
- lib/bas/mapper/base.rb
|
75
|
+
- lib/bas/mapper/github/issues.rb
|
71
76
|
- lib/bas/mapper/imap/support_emails.rb
|
72
77
|
- lib/bas/mapper/notion/birthday_today.rb
|
73
78
|
- lib/bas/mapper/notion/pto_today.rb
|