Skip to content

Commit

Permalink
Add fetch next week ptos from notion bot (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
FelipeGuzmanSierra authored May 8, 2024
1 parent ab0ec4c commit f7e108f
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 0 deletions.
162 changes: 162 additions & 0 deletions lib/v2/bot/fetch_next_week_ptos_from_notion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# frozen_string_literal: true

require_relative "./base"
require_relative "../read/default"
require_relative "../utils/notion/request"
require_relative "../write/postgres"

module Bot
##
# The Bot::FetchNextWeekPtosFromNotion class serves as a bot implementation to read next week
# PTO's from a notion database and write them on a PostgresDB table with a specific format.
#
# <br>
# <b>Example</b>
#
# options = {
# process_options: {
# database_id: "notion database id",
# secret: "notion secret"
# },
# write_options: {
# connection: {
# host: "host",
# port: 5432,
# dbname: "bas",
# user: "postgres",
# password: "postgres"
# },
# db_table: "pto",
# bot_name: "FetchNextWeekPtosFromNotion"
# }
# }
#
# bot = Bot::FetchNextWeekPtosFromNotion.new(options)
# bot.execute
#
class FetchNextWeekPtosFromNotion < Bot::Base
# Read function to execute the default Read component
#
def read
reader = Read::Default.new

reader.execute
end

# Process function to execute the Notion utility to fetch next week PTO's from the notion database
#
def process(_read_response)
response = Utils::Notion::Request.execute(params)

if response.code == 200
ptos_list = normalize_response(response.parsed_response["results"])

{ success: { ptos: ptos_list } }
else
{ error: { message: response.parsed_response, status_code: response.code } }
end
end

# Write function to execute the PostgresDB write component
#
def write(process_response)
write = Write::Postgres.new(write_options, process_response)

write.execute
end

private

def params
{
endpoint: "databases/#{process_options[:database_id]}/query",
secret: process_options[:secret],
method: "post",
body:
}
end

def body
monday, sunday = next_week_dates

{
filter: {
or: [
belong_next_week("StartDateTime", monday, sunday),
belong_next_week("EndDateTime", monday, sunday),
cover_next_week(monday, sunday)
]
}
}
end

def next_week_dates
monday = next_week_monday
sunday = monday + 6

[monday, sunday]
end

def next_week_monday
today = Date.today
week_day = today.wday

days = week_day.zero? ? 1 : 8 - week_day

today + days
end

def belong_next_week(property, after_day, before_day)
{
and: [
{ property:, date: { on_or_after: after_day } },
{ property:, date: { on_or_before: before_day } }
]
}
end

def cover_next_week(monday, sunday)
{
and: [
{ property: "EndDateTime", date: { on_or_after: sunday } },
{ property: "StartDateTime", date: { on_or_before: monday } }
]
}
end

def normalize_response(results)
return [] if results.nil?

results.map do |pto|
pto_fields = pto["properties"]

{
"Name" => extract_description_field_value(pto_fields["Description"]),
"StartDateTime" => extract_date_field_value(pto_fields["StartDateTime"]),
"EndDateTime" => extract_date_field_value(pto_fields["EndDateTime"])
}
end
end

def extract_description_field_value(data)
names = data["title"].map { |name| name["plain_text"] }

names.join(" ")
end

def extract_date_field_value(date)
{
from: extract_start_date(date),
to: extract_end_date(date)
}
end

def extract_start_date(data)
data["date"]["start"]
end

def extract_end_date(data)
data["date"]["end"]
end
end
end
125 changes: 125 additions & 0 deletions spec/v2/bot/fetch_next_week_ptos_from_notion_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# frozen_string_literal: true

require "v2/bot/fetch_next_week_ptos_from_notion"

RSpec.describe Bot::FetchNextWeekPtosFromNotion do
before do
config = {
process_options: {
database_id: "database_id",
secret: "secret"
},
write_options: {
connection: {
host: "host",
port: 5432,
dbname: "bas",
user: "postgres",
password: "postgres"
},
db_table: "pto",
bot_name: "FetchPtosFromNotion"
}
}

@bot = described_class.new(config)
end

describe "attributes and arguments" do
it { expect(described_class).to respond_to(:new).with(1).arguments }

it { expect(@bot).to respond_to(:execute).with(0).arguments }
it { expect(@bot).to respond_to(:read).with(0).arguments }
it { expect(@bot).to respond_to(:process).with(1).arguments }
it { expect(@bot).to respond_to(:write).with(1).arguments }

it { expect(@bot).to respond_to(:read_options) }
it { expect(@bot).to respond_to(:process_options) }
it { expect(@bot).to respond_to(:write_options) }
end

describe ".read" do
it { expect(@bot.read).to be_a Read::Types::Response }
end

describe ".process" do
let(:pto) do
{
"properties" => {
"Description" => { "title" => [{ "plain_text" => "John Doe" }] },
"StartDateTime" => { "date" => { "start" => "2024-05-01", "end" => "" } },
"EndDateTime" => { "date" => { "start" => "2024-05-02", "end" => "" } }
}
}
end

let(:formatted_pto) do
{
"Name" => "John Doe",
"StartDateTime" => { from: "2024-05-01", to: "" },
"EndDateTime" => { from: "2024-05-02", to: "" }
}
end

let(:error_response) { { "object" => "error", "status" => 404, "message" => "not found" } }

let(:response) { double("http_response") }

before do
@read_response = Read::Types::Response.new

allow(HTTParty).to receive(:send).and_return(response)
end

it "returns a success hash with the list of formatted ptos" do
allow(response).to receive(:code).and_return(200)
allow(response).to receive(:parsed_response).and_return({ "results" => [pto] })

processed = @bot.process(@read_response)

expect(processed).to eq({ success: { ptos: [formatted_pto] } })
end

it "returns an error hash with the error message" do
allow(response).to receive(:code).and_return(404)
allow(response).to receive(:parsed_response).and_return(error_response)

processed = @bot.process(@read_response)

expect(processed).to eq({ error: { message: error_response, status_code: 404 } })
end
end

describe ".write" do
let(:pg_conn) { instance_double(PG::Connection) }

let(:formatted_pto) do
{
"Name" => "John Doe",
"StartDateTime" => { from: "2024-05-01", to: "" },
"EndDateTime" => { from: "2024-05-02", to: "" }
}
end

let(:error_response) { { "object" => "error", "status" => 404, "message" => "not found" } }

before do
pg_result = instance_double(PG::Result)

allow(PG::Connection).to receive(:new).and_return(pg_conn)
allow(pg_conn).to receive(:exec_params).and_return(pg_result)
end

it "save the process success response in a postgres table" do
process_response = { success: { ptos: [formatted_pto] } }

expect(@bot.write(process_response)).to_not be_nil
end

it "save the process fail response in a postgres table" do
process_response = { error: { message: error_response, status_code: 404 } }

expect(@bot.write(process_response)).to_not be_nil
end
end
end

0 comments on commit f7e108f

Please sign in to comment.