bns 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +11 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/CONTRIBUTING.md +66 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +91 -0
- data/LICENSE +21 -0
- data/README.md +198 -0
- data/Rakefile +12 -0
- data/lib/bns/dispatcher/base.rb +31 -0
- data/lib/bns/dispatcher/discord/exceptions/invalid_webhook_token.rb +16 -0
- data/lib/bns/dispatcher/discord/implementation.rb +51 -0
- data/lib/bns/dispatcher/discord/types/response.rb +22 -0
- data/lib/bns/domain/birthday.rb +23 -0
- data/lib/bns/domain/exceptions/function_not_implemented.rb +18 -0
- data/lib/bns/domain/pto.rb +26 -0
- data/lib/bns/fetcher/base.rb +30 -0
- data/lib/bns/fetcher/notion/birthday.rb +53 -0
- data/lib/bns/fetcher/notion/exceptions/invalid_api_key.rb +15 -0
- data/lib/bns/fetcher/notion/exceptions/invalid_database_id.rb +15 -0
- data/lib/bns/fetcher/notion/helper.rb +21 -0
- data/lib/bns/fetcher/notion/pto.rb +48 -0
- data/lib/bns/fetcher/notion/types/response.rb +26 -0
- data/lib/bns/formatter/base.rb +29 -0
- data/lib/bns/formatter/discord/birthday.rb +43 -0
- data/lib/bns/formatter/discord/exceptions/invalid_data.rb +17 -0
- data/lib/bns/formatter/discord/pto.rb +52 -0
- data/lib/bns/mapper/base.rb +30 -0
- data/lib/bns/mapper/notion/birthday.rb +76 -0
- data/lib/bns/mapper/notion/pto.rb +96 -0
- data/lib/bns/use_cases/types/config.rb +19 -0
- data/lib/bns/use_cases/use_case.rb +39 -0
- data/lib/bns/use_cases/use_cases.rb +150 -0
- data/lib/bns/version.rb +6 -0
- data/lib/bns.rb +9 -0
- data/sig/business_notification_system.rbs +4 -0
- metadata +84 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
require_relative "./exceptions/invalid_webhook_token"
|
5
|
+
require_relative "./types/response"
|
6
|
+
|
7
|
+
module Dispatcher
|
8
|
+
module Discord
|
9
|
+
##
|
10
|
+
# This class is an implementation of the Dispatcher::Base interface, specifically designed
|
11
|
+
# for dispatching messages to Discord.
|
12
|
+
#
|
13
|
+
class Implementation < Base
|
14
|
+
# Implements the dispatching logic for the Discord use case. It sends a POST request to
|
15
|
+
# the Discord webhook with the specified payload.
|
16
|
+
#
|
17
|
+
# <br>
|
18
|
+
# <b>Params:</b>
|
19
|
+
# * <tt>String</tt> payload: Payload to be dispatched to discord.
|
20
|
+
# <br>
|
21
|
+
# <b>raises</b> <tt>Exceptions::Discord::InvalidWebookToken</tt> if the provided webhook token is invalid.
|
22
|
+
#
|
23
|
+
# <br>
|
24
|
+
# <b>returns</b> <tt>Dispatcher::Discord::Types::Response</tt>
|
25
|
+
#
|
26
|
+
def dispatch(payload)
|
27
|
+
body = {
|
28
|
+
username: name,
|
29
|
+
avatar_url: "",
|
30
|
+
content: payload
|
31
|
+
}.to_json
|
32
|
+
response = HTTParty.post(webhook, { body: body, headers: { "Content-Type" => "application/json" } })
|
33
|
+
|
34
|
+
discord_response = Dispatcher::Discord::Types::Response.new(response)
|
35
|
+
|
36
|
+
validate_response(discord_response)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def validate_response(response)
|
42
|
+
case response.code
|
43
|
+
when 50_027
|
44
|
+
raise Exceptions::Discord::InvalidWebookToken, response.message
|
45
|
+
else
|
46
|
+
response
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dispatcher
|
4
|
+
module Discord
|
5
|
+
module Types
|
6
|
+
##
|
7
|
+
# Represents a response received from Discord. It encapsulates essential information about the response,
|
8
|
+
# providing a structured way to handle and analyze Discord server responses.
|
9
|
+
#
|
10
|
+
class Response
|
11
|
+
attr_reader :code, :http_code, :message, :response
|
12
|
+
|
13
|
+
def initialize(response)
|
14
|
+
@http_code = response.code
|
15
|
+
@code = response["code"]
|
16
|
+
@message = response.message
|
17
|
+
@response = response.response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
##
|
5
|
+
# The Domain::Birthday class provides a domain-specific representation of a Birthday object.
|
6
|
+
# It encapsulates the individual's name and their birthdate, offering a structured way to
|
7
|
+
# handle and manipulate birthday information.
|
8
|
+
class Birthday
|
9
|
+
attr_reader :individual_name, :birth_date
|
10
|
+
|
11
|
+
# Initializes a Domain::Birthday instance with the specified individual name, and date of birth.
|
12
|
+
#
|
13
|
+
# <br>
|
14
|
+
# <b>Params:</b>
|
15
|
+
# * <tt>String</tt> individual_name Name of the individual
|
16
|
+
# * <tt>Date</tt> birth_date Birthdate from the individual
|
17
|
+
#
|
18
|
+
def initialize(individual_name, date)
|
19
|
+
@individual_name = individual_name
|
20
|
+
@birth_date = date
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
module Exceptions
|
5
|
+
##
|
6
|
+
# Provides a domain-specific representation for errors that occur when a function has not been implemented yet.
|
7
|
+
# It inherits from StandardError # and allows developers to raise a specific exception when a required function
|
8
|
+
# remains unimplemented in a subclass.
|
9
|
+
#
|
10
|
+
class FunctionNotImplemented < StandardError
|
11
|
+
# Initializes the exception with an optional custom error message.
|
12
|
+
#
|
13
|
+
def initialize(message = "The function haven't been implemented yet.")
|
14
|
+
super(message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
##
|
5
|
+
# The Domain::Pto class provides a domain-specific representation of a Paid Time Off (PTO) object.
|
6
|
+
# It encapsulates information about an individual's time off, including the individual's name,
|
7
|
+
# the start date, and the end date of the time off period.
|
8
|
+
#
|
9
|
+
class Pto
|
10
|
+
attr_reader :individual_name, :start_date, :end_date
|
11
|
+
|
12
|
+
# Initializes a Domain::Pto instance with the specified individual name, start date, and end date.
|
13
|
+
#
|
14
|
+
# <br>
|
15
|
+
# <b>Params:</b>
|
16
|
+
# * <tt>String</tt> individual_name Name of the individual.
|
17
|
+
# * <tt>DateTime</tt> start_date Start day of the PTO.
|
18
|
+
# * <tt>String</tt> end_date End date of the PTO.
|
19
|
+
#
|
20
|
+
def initialize(individual_name, start_date, end_date)
|
21
|
+
@individual_name = individual_name
|
22
|
+
@start_date = start_date
|
23
|
+
@end_date = end_date
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/exceptions/function_not_implemented"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
##
|
7
|
+
# The Fetcher::Base class serves as the foundation for implementing specific data fetchers within the Fetcher module.
|
8
|
+
# Operating as an interface, this class defines essential attributes and methods, providing a blueprint for creating
|
9
|
+
# custom fetchers tailored to different data sources.
|
10
|
+
#
|
11
|
+
class Base
|
12
|
+
attr_reader :config
|
13
|
+
|
14
|
+
# Initializes the fetcher with essential configuration parameters.
|
15
|
+
#
|
16
|
+
def initialize(config)
|
17
|
+
@config = config
|
18
|
+
end
|
19
|
+
|
20
|
+
# A method meant to fetch data from an specific source depending on the implementation.
|
21
|
+
# Must be overridden by subclasses, with specific logic based on the use case.
|
22
|
+
#
|
23
|
+
# <br>
|
24
|
+
# <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
|
25
|
+
#
|
26
|
+
def fetch
|
27
|
+
raise Domain::Exceptions::FunctionNotImplemented
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "date"
|
5
|
+
|
6
|
+
require_relative "../base"
|
7
|
+
require_relative "./exceptions/invalid_api_key"
|
8
|
+
require_relative "./exceptions/invalid_database_id"
|
9
|
+
require_relative "./types/response"
|
10
|
+
require_relative "./helper"
|
11
|
+
|
12
|
+
module Fetcher
|
13
|
+
module Notion
|
14
|
+
##
|
15
|
+
# This class is an implementation of the Fetcher::Base interface, specifically designed
|
16
|
+
# for fetching birthday data from Notion.
|
17
|
+
#
|
18
|
+
class Birthday < Base
|
19
|
+
# Implements the data fetching logic for Birthdays data from Notion. It sends a POST
|
20
|
+
# request to the Notion API to query the specified database and returns a validated response.
|
21
|
+
#
|
22
|
+
# <br>
|
23
|
+
# <b>raises</b> <tt>Exceptions::Notion::InvalidApiKey</tt> if the API key provided is incorrect or invalid.
|
24
|
+
#
|
25
|
+
# <b>raises</b> <tt>Exceptions::Notion::InvalidDatabaseId</tt> if the Database id provided is incorrect
|
26
|
+
# or invalid.
|
27
|
+
#
|
28
|
+
def fetch
|
29
|
+
url = build_url(config[:base_url], config[:database_id])
|
30
|
+
|
31
|
+
httparty_response = HTTParty.post(url, { body: config[:filter].to_json, headers: headers })
|
32
|
+
|
33
|
+
notion_response = Fetcher::Notion::Types::Response.new(httparty_response)
|
34
|
+
|
35
|
+
Fetcher::Notion::Helper.validate_response(notion_response)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def headers
|
41
|
+
{
|
42
|
+
"Authorization" => "Bearer #{config[:secret]}",
|
43
|
+
"Content-Type" => "application/json",
|
44
|
+
"Notion-Version" => "2022-06-28"
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_url(base, database_id)
|
49
|
+
"#{base}/v1/databases/#{database_id}/query"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Exceptions
|
4
|
+
module Notion
|
5
|
+
##
|
6
|
+
# Provides a domain-specific representation for errors that occurs when an invalid API key is provided
|
7
|
+
# for a Notion-related operation.
|
8
|
+
#
|
9
|
+
class InvalidApiKey < StandardError
|
10
|
+
def initialize(message = "The provided API token is invalid.")
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Exceptions
|
4
|
+
module Notion
|
5
|
+
##
|
6
|
+
# Provides a domain-specific representation for errors that occurs when an invalid database id is provided
|
7
|
+
# for a Notion-related operation.
|
8
|
+
#
|
9
|
+
class InvalidDatabaseId < StandardError
|
10
|
+
def initialize(message = "The provided id doesn't match any database.")
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
module Notion
|
5
|
+
##
|
6
|
+
# Provides common fuctionalities along the Notion domain.
|
7
|
+
#
|
8
|
+
module Helper
|
9
|
+
def self.validate_response(response)
|
10
|
+
case response.status_code
|
11
|
+
when 401
|
12
|
+
raise Exceptions::Notion::InvalidApiKey, response.message
|
13
|
+
when 404
|
14
|
+
raise Exceptions::Notion::InvalidDatabaseId, response.message
|
15
|
+
else
|
16
|
+
response
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "date"
|
5
|
+
|
6
|
+
require_relative "../base"
|
7
|
+
require_relative "./exceptions/invalid_api_key"
|
8
|
+
require_relative "./exceptions/invalid_database_id"
|
9
|
+
require_relative "./types/response"
|
10
|
+
|
11
|
+
module Fetcher
|
12
|
+
module Notion
|
13
|
+
##
|
14
|
+
# This class is an implementation of the Fetcher::Base interface, specifically designed
|
15
|
+
# for fetching Paid Time Off (PTO) data from Notion.
|
16
|
+
#
|
17
|
+
class Pto < Base
|
18
|
+
# Implements the data fetching logic for PTO's data from Notion. It sends a POST
|
19
|
+
# request to the Notion API to query the specified database and returns a validated response.
|
20
|
+
#
|
21
|
+
# <br>
|
22
|
+
# <b>raises</b> <tt>Exceptions::Notion::InvalidApiKey</tt> if the API key provided is incorrect or invalid.
|
23
|
+
#
|
24
|
+
# <b>raises</b> <tt>Exceptions::Notion::InvalidDatabaseId</tt> if the Database id provided is incorrect
|
25
|
+
# or invalid.
|
26
|
+
#
|
27
|
+
def fetch
|
28
|
+
url = "#{config[:base_url]}/v1/databases/#{config[:database_id]}/query"
|
29
|
+
|
30
|
+
httparty_response = HTTParty.post(url, { body: config[:filter].to_json, headers: headers })
|
31
|
+
|
32
|
+
notion_response = Fetcher::Notion::Types::Response.new(httparty_response)
|
33
|
+
|
34
|
+
Fetcher::Notion::Helper.validate_response(notion_response)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def headers
|
40
|
+
{
|
41
|
+
"Authorization" => "Bearer #{config[:secret]}",
|
42
|
+
"Content-Type" => "application/json",
|
43
|
+
"Notion-Version" => "2022-06-28"
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
module Notion
|
5
|
+
module Types
|
6
|
+
##
|
7
|
+
# Represents a response received from the Notion API. It encapsulates essential information about the response,
|
8
|
+
# providing a structured way to handle and analyze it's responses.
|
9
|
+
class Response
|
10
|
+
attr_reader :status_code, :message, :results
|
11
|
+
|
12
|
+
def initialize(response)
|
13
|
+
if response["results"].nil?
|
14
|
+
@status_code = response["status"]
|
15
|
+
@message = response["message"]
|
16
|
+
@results = nil
|
17
|
+
else
|
18
|
+
@status_code = 200
|
19
|
+
@message = "success"
|
20
|
+
@results = response["results"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/exceptions/function_not_implemented"
|
4
|
+
|
5
|
+
module Formatter
|
6
|
+
##
|
7
|
+
# The Formatter::Base module serves as the foundation for implementing specific data presentation logic
|
8
|
+
# within the Formatter module. Defines essential methods, that provide a blueprint for creating custom
|
9
|
+
# formatters tailored to different use cases.
|
10
|
+
#
|
11
|
+
module Base
|
12
|
+
# A method meant to give an specified format depending on the implementation to the data coming from an
|
13
|
+
# implementation of the Mapper::Base interface.
|
14
|
+
# Must be overridden by subclasses, with specific logic based on the use case.
|
15
|
+
#
|
16
|
+
# <br>
|
17
|
+
# <b>Params:</b>
|
18
|
+
# * <tt>List<Domain::></tt> domain_data: List of specific domain objects depending on the use case.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
|
22
|
+
#
|
23
|
+
# <b>returns</b> <tt>String</tt> Formatted payload suitable for a Dispatcher::Base implementation.
|
24
|
+
#
|
25
|
+
def format(_domain_data)
|
26
|
+
raise Domain::Exceptions::FunctionNotImplemented
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/birthday"
|
4
|
+
require_relative "../base"
|
5
|
+
require_relative "./exceptions/invalid_data"
|
6
|
+
|
7
|
+
module Formatter
|
8
|
+
module Discord
|
9
|
+
##
|
10
|
+
# This class implementats the methods of the Formatter::Base module, specifically designed for formatting birthday
|
11
|
+
# data in a way suitable for Discord messages.
|
12
|
+
class Birthday
|
13
|
+
include Base
|
14
|
+
|
15
|
+
# Implements the logic for building a formatted payload with the given template for birthdays.
|
16
|
+
#
|
17
|
+
# <br>
|
18
|
+
# <b>Params:</b>
|
19
|
+
# * <tt>List<Domain::Birthday></tt> birthdays_list: list of mapped birthdays.
|
20
|
+
#
|
21
|
+
# <br>
|
22
|
+
# <b>raises</b> <tt>Formatter::Discord::Exceptions::InvalidData</tt> when invalid data is provided.
|
23
|
+
#
|
24
|
+
# <br>
|
25
|
+
# <b>returns</b> <tt>String</tt> payload: formatted payload suitable for a Discord message.
|
26
|
+
#
|
27
|
+
def format(birthdays_list)
|
28
|
+
raise Formatter::Discord::Exceptions::InvalidData unless birthdays_list.all? do |brithday|
|
29
|
+
brithday.is_a?(Domain::Birthday)
|
30
|
+
end
|
31
|
+
|
32
|
+
template = "NAME, Wishing you a very happy birthday! Enjoy your special day! :birthday: :gift:"
|
33
|
+
payload = ""
|
34
|
+
|
35
|
+
birthdays_list.each do |birthday|
|
36
|
+
payload += "#{template.gsub("NAME", birthday.individual_name)}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
payload
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formatter
|
4
|
+
module Discord
|
5
|
+
module Exceptions
|
6
|
+
##
|
7
|
+
# Provides a domain-specific representation for errors that occurs when trying to process invalid
|
8
|
+
# data on a Fetcher::Base implementation
|
9
|
+
#
|
10
|
+
class InvalidData < StandardError
|
11
|
+
def initialize(message = "")
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/pto"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Formatter
|
7
|
+
module Discord
|
8
|
+
##
|
9
|
+
# This class is an implementation of the Formatter::Base interface, specifically designed for formatting PTO
|
10
|
+
# data in a way suitable for Discord messages.
|
11
|
+
class Pto
|
12
|
+
include Base
|
13
|
+
|
14
|
+
# Implements the logic for building a formatted payload with the given template for PTO's.
|
15
|
+
#
|
16
|
+
# <br>
|
17
|
+
# <b>Params:</b>
|
18
|
+
# * <tt>List<Domain::Pto></tt> pto_list: List of mapped PTO's.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>raises</b> <tt>Formatter::Discord::Exceptions::InvalidData</tt> when invalid data is provided.
|
22
|
+
#
|
23
|
+
# <br>
|
24
|
+
# <b>returns</b> <tt>String</tt> payload, formatted payload suitable for a Discord message.
|
25
|
+
#
|
26
|
+
def format(ptos_list)
|
27
|
+
raise Formatter::Discord::Exceptions::InvalidData unless ptos_list.all? { |pto| pto.is_a?(Domain::Pto) }
|
28
|
+
|
29
|
+
template = ":beach: NAME is on PTO"
|
30
|
+
payload = ""
|
31
|
+
|
32
|
+
ptos_list.each do |pto|
|
33
|
+
payload += "#{template.gsub("NAME", pto.individual_name)} #{build_pto_message(pto)}\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
payload
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def build_pto_message(pto)
|
42
|
+
if pto.start_date.include?("|")
|
43
|
+
start_time = pto.start_date.split("|")
|
44
|
+
end_time = pto.end_date.split("|")
|
45
|
+
"#{start_time[1]} - #{end_time[1]}"
|
46
|
+
else
|
47
|
+
"all day"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/exceptions/function_not_implemented"
|
4
|
+
|
5
|
+
module Mapper
|
6
|
+
##
|
7
|
+
# The Mapper::Base module serves as the foundation for implementing specific data shaping logic within the
|
8
|
+
# Mapper module. Defines essential methods, that provide a blueprint for organizing or shaping data in a manner
|
9
|
+
# suitable for downstream formatting processes.
|
10
|
+
#
|
11
|
+
module Base
|
12
|
+
# An method meant to prepare or organize the data coming from an implementation of the Fetcher::Base interface.
|
13
|
+
# Must be overridden by subclasses, with specific logic based on the use case.
|
14
|
+
#
|
15
|
+
# <br>
|
16
|
+
# <b>Params:</b>
|
17
|
+
# * <tt>Fetcher::Notion::Types::Response</tt> response: Response produced by a fetcher.
|
18
|
+
#
|
19
|
+
# <br>
|
20
|
+
#
|
21
|
+
# <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
|
22
|
+
# <br>
|
23
|
+
#
|
24
|
+
# <b>returns</b> <tt>List<Domain::></tt> Mapped list of data, ready to be formatted.
|
25
|
+
#
|
26
|
+
def map(_response)
|
27
|
+
raise Domain::Exceptions::FunctionNotImplemented
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/birthday"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
module Notion
|
8
|
+
##
|
9
|
+
# This class implementats the methods of the Mapper::Base module, specifically designed for preparing or
|
10
|
+
# shaping birthdays data coming from a Fetcher::Base implementation.
|
11
|
+
class Birthday
|
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::Notion::Types::Response</tt> notion_response: Notion response object.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>return</b> <tt>List<Domain::Birthday></tt> birthdays_list, mapped birthdays to be used by a
|
22
|
+
# Formatter::Base implementation.
|
23
|
+
#
|
24
|
+
def map(notion_response)
|
25
|
+
return [] if notion_response.results.empty?
|
26
|
+
|
27
|
+
normalized_notion_data = normalize_response(notion_response.results)
|
28
|
+
|
29
|
+
normalized_notion_data.map do |birthday|
|
30
|
+
Domain::Birthday.new(birthday["name"], birthday["birth_date"])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalize_response(results)
|
37
|
+
return [] if results.nil?
|
38
|
+
|
39
|
+
normalized_results = []
|
40
|
+
|
41
|
+
results.map do |value|
|
42
|
+
properties = value["properties"]
|
43
|
+
properties.delete("Name")
|
44
|
+
|
45
|
+
normalized_value = normalize(properties)
|
46
|
+
|
47
|
+
normalized_results.append(normalized_value)
|
48
|
+
end
|
49
|
+
|
50
|
+
normalized_results
|
51
|
+
end
|
52
|
+
|
53
|
+
def normalize(properties)
|
54
|
+
normalized_value = {}
|
55
|
+
|
56
|
+
properties.each do |k, v|
|
57
|
+
if k == "Complete Name"
|
58
|
+
normalized_value["name"] = extract_rich_text_field_value(v)
|
59
|
+
elsif k == "BD_this_year"
|
60
|
+
normalized_value["birth_date"] = extract_date_field_value(v)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
normalized_value
|
65
|
+
end
|
66
|
+
|
67
|
+
def extract_rich_text_field_value(data)
|
68
|
+
data["rich_text"][0]["plain_text"]
|
69
|
+
end
|
70
|
+
|
71
|
+
def extract_date_field_value(data)
|
72
|
+
data["formula"]["date"]["start"]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|