bns 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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/._.rspec_status +0 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +2 -0
  5. data/README.md +163 -4
  6. data/SECURITY.md +13 -0
  7. data/lib/bns/dispatcher/discord/exceptions/invalid_webhook_token.rb +2 -2
  8. data/lib/bns/dispatcher/discord/implementation.rb +1 -1
  9. data/lib/bns/dispatcher/slack/exceptions/invalid_webhook_token.rb +16 -0
  10. data/lib/bns/dispatcher/slack/implementation.rb +51 -0
  11. data/lib/bns/dispatcher/slack/types/response.rb +21 -0
  12. data/lib/bns/domain/birthday.rb +2 -0
  13. data/lib/bns/domain/pto.rb +2 -0
  14. data/lib/bns/domain/work_items_limit.rb +39 -0
  15. data/lib/bns/fetcher/base.rb +13 -0
  16. data/lib/bns/fetcher/notion/{pto.rb → base.rb} +11 -7
  17. data/lib/bns/fetcher/notion/types/response.rb +1 -1
  18. data/lib/bns/fetcher/notion/use_case/birthday_next_week.rb +41 -0
  19. data/lib/bns/fetcher/notion/use_case/birthday_today.rb +29 -0
  20. data/lib/bns/fetcher/notion/use_case/pto_next_week.rb +71 -0
  21. data/lib/bns/fetcher/notion/use_case/pto_today.rb +30 -0
  22. data/lib/bns/fetcher/notion/use_case/work_items_limit.rb +37 -0
  23. data/lib/bns/fetcher/postgres/base.rb +46 -0
  24. data/lib/bns/fetcher/postgres/helper.rb +16 -0
  25. data/lib/bns/fetcher/postgres/types/response.rb +42 -0
  26. data/lib/bns/fetcher/postgres/use_case/pto_today.rb +32 -0
  27. data/lib/bns/formatter/base.rb +27 -3
  28. data/lib/bns/formatter/birthday.rb +34 -0
  29. data/lib/bns/formatter/exceptions/invalid_data.rb +15 -0
  30. data/lib/bns/formatter/pto.rb +76 -0
  31. data/lib/bns/formatter/work_items_limit.rb +43 -0
  32. data/lib/bns/mapper/notion/{birthday.rb → birthday_today.rb} +13 -21
  33. data/lib/bns/mapper/notion/{pto.rb → pto_today.rb} +15 -41
  34. data/lib/bns/mapper/notion/work_items_limit.rb +65 -0
  35. data/lib/bns/mapper/postgres/pto_today.rb +47 -0
  36. data/lib/bns/use_cases/use_cases.rb +227 -49
  37. data/lib/bns/version.rb +1 -1
  38. data/renovate.json +6 -0
  39. metadata +27 -10
  40. data/Gemfile.lock +0 -91
  41. data/lib/bns/fetcher/notion/birthday.rb +0 -53
  42. data/lib/bns/formatter/discord/birthday.rb +0 -43
  43. data/lib/bns/formatter/discord/exceptions/invalid_data.rb +0 -17
  44. data/lib/bns/formatter/discord/pto.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a422fa03f80a83a9c8ba021d998a00b90ee7381a5167d96e23562c67cc6d73d1
4
- data.tar.gz: 168f81ac480dc71796c2123c24ad27a5b04cbd1a9543388c1010425a037ad08f
3
+ metadata.gz: 7f27d7368a358fcf6dea96e51922690ff399670b5582387b70f56db0d5b25feb
4
+ data.tar.gz: a46adf0a088dd65740c85aadc5d3005654f3654e2f12cecf345700be7f44f2b1
5
5
  SHA512:
6
- metadata.gz: 28919c76616533281768cd65bb00e4e2d0d7c0e22c480ffe38bf75d23e5d46a3b768f3e1e99bc68e55b3d822eb01fd598731e12e35351f8b29a15b60f80bee3d
7
- data.tar.gz: d88313f8d80abce0480c63a4e35d8b5ccfa0151c2afb13205b2ae31a755974d72f2be87fdd726ac573b6b80ac4f1fcd133899936b04728af70e95d3be4a358e7
6
+ metadata.gz: bfa9c1394d5fe161f90fe86e29a44acace8b2a7809ee8bd233ee12d0be082b11a0081e3fb98efefcdfe22c64a50f27335a95ac24f9c8baeec10c5a9f8d381754
7
+ data.tar.gz: a13978067c0c9ad5a96c8abc8ae2f7781674a0f4b3accc9b2229a11dde525f706a52ee448effad8570c34b2141a5e9c3ad0a3457f29dea2f5d60d42c6ba7bd4e
data/._.rspec_status ADDED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (29.02.2024)
4
+ - [Add a Postgres fetcher component](https://github.com/kommitters/bns/issues/28)
5
+ - [Use case - PTO's Postgres-Slack implementation](https://github.com/kommitters/bns/issues/30)
6
+ - [Slack dispatcher](https://github.com/kommitters/bns/issues/32)
7
+ - [Refactor fetcher components](https://github.com/kommitters/bns/issues/34)
8
+ - [Add Use Case - Boards WI alerts](https://github.com/kommitters/bns/issues/36)
9
+ - [Add Use Case - Next Week Birthday](https://github.com/kommitters/bns/issues/38)
10
+ - [Add Use Case - Next Week PTO's](https://github.com/kommitters/bns/issues/39)
11
+
12
+ ## 0.1.1 (07.02.2024)
13
+ - [Add custom templates option](https://github.com/kommitters/bns/issues/25)
14
+ - [PTO's formatting use cases](https://github.com/kommitters/bns/issues/24)
15
+
3
16
  ## 0.1.0 (06.02.2024)
4
17
 
5
18
  - [Build initial codebase](https://github.com/kommitters/bns/issues/6)
data/Gemfile CHANGED
@@ -16,3 +16,5 @@ gem "vcr"
16
16
  gem "webmock"
17
17
 
18
18
  gem "httparty"
19
+
20
+ gem "pg", "~> 1.5", ">= 1.5.4"
data/README.md CHANGED
@@ -1,11 +1,16 @@
1
1
  # BNS - Business Notification System
2
2
 
3
- The business notification system is designed to be a versatile platform, offering key components for building various use cases. It provides an easy-to-use tool for implementing notifications without excessive complexity.
3
+ Many organizations and individuals rely on automatic notifications across various contexts in their daily operations. With BNS, we aim to provide an open-source platform that empowers users to create customized notification systems tailored to their unique requirements. BNS consists of a series of abstract components designed to facilitate the creation of diverse use cases, regardless of context.
4
4
 
5
+ The underlying idea is to develop generic components that can serve a wide range of needs, this approach ensures that all members of the community can leverage the platform's evolving suite of components and use cases to their advantage.
6
+
7
+ ![Gem Version](https://img.shields.io/gem/v/bns?style=for-the-badge)
8
+ ![Gem Total Downloads](https://img.shields.io/gem/dt/bns?style=for-the-badge)
5
9
  ![Build Badge](https://img.shields.io/github/actions/workflow/status/kommitters/bns/ci.yml?branch=project-opensource-config&style=for-the-badge)
6
10
  [![Coverage Status](https://img.shields.io/coveralls/github/kommitters/bns?style=for-the-badge)](https://coveralls.io/github/kommitters/bns?branch=main)
7
- [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/kommitters/bns?style=for-the-badge)](https://api.securityscorecards.dev/projects/github.com/kommitters/bns)
8
11
  ![GitHub License](https://img.shields.io/github/license/kommitters/bns?style=for-the-badge)
12
+ [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/kommitters/bns?label=openssf%20scorecard&style=for-the-badge)](https://api.securityscorecards.dev/projects/github.com/kommitters/bns)
13
+ [![OpenSSF Best Practices](https://img.shields.io/cii/summary/8383?label=openssf%20best%20practices&style=for-the-badge)](https://bestpractices.coreinfrastructure.org/projects/8383)
9
14
 
10
15
  ## Installation
11
16
 
@@ -147,9 +152,163 @@ use_case.perform
147
152
 
148
153
  ```
149
154
 
150
- **Serverless**
155
+ ### Serverless
156
+ We'll explain how to configure and deploy a use case with serverless, this example will cover the PTO's notifications use case.
157
+
158
+ #### Configuring environment variables
159
+ Create the environment variables configuration file.
160
+
161
+ ```bash
162
+ cp env.yml.example env.yml
163
+ ```
151
164
 
152
- Examples of different use cases, and how to configure and deploy the lambdas can be found on: `https://github.com/kommitters/bns_serverless`
165
+ And put the following env variables
166
+ ```
167
+ dev:
168
+ NOTION_DATABASE_ID: NOTION_DATABASE_ID
169
+ NOTION_SECRET: NOTION_SECRET
170
+ DISCORD_WEBHOOK: DISCORD_WEBHOOK
171
+ DISCORD_BOT_NAME: DISCORD_BOT_NAME
172
+ prod:
173
+ NOTION_DATABASE_ID: NOTION_DATABASE_ID
174
+ NOTION_SECRET: NOTION_SECRET
175
+ DISCORD_WEBHOOK: DISCORD_WEBHOOK
176
+ DISCORD_BOT_NAME: DISCORD_BOT_NAME
177
+
178
+ ```
179
+
180
+ The variables should be defined either in the custom settings section within the `serverless.yml` file to ensure accessibility by all lambdas, or in the environment configuration option for each lambda respectively. For example:
181
+
182
+ ```bash
183
+ # Accessible by all the lambdas
184
+ custom:
185
+ settings:
186
+ api:
187
+ NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.NOTION_DATABASE_ID}
188
+ NOTION_SECRET: ${file(./env.yml):${env:STAGE}.NOTION_SECRET}}
189
+
190
+ # Accessible by the lambda
191
+ functions:
192
+ lambdaName:
193
+ environment:
194
+ NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.NOTION_DATABASE_ID}
195
+ NOTION_SECRET: ${file(./env.yml):${env:STAGE}.NOTION_SECRET}}
196
+ ```
197
+
198
+ #### Schedule
199
+ the schedule is configured using an environment variable containing the cron configuration. For example:
200
+ ```bash
201
+ # env.yml file
202
+ SCHEDULER: cron(0 13 ? * MON-FRI *)
203
+
204
+ # serverless.yml
205
+ functions:
206
+ lambdaName:
207
+ events:
208
+ - schedule:
209
+ rate: ${file(./env.yml):${env:STAGE}.SCHEDULER}
210
+ ```
211
+
212
+ To learn how to modify the cron configuration follow this guide: [Schedule expressions using rate or cron](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html)
213
+
214
+ #### Building your lambda
215
+ On your serverless configuration, create your lambda function, on your serverless `/src` folder.
216
+
217
+ ```ruby
218
+ # frozen_string_literal: true
219
+
220
+ require 'bns'
221
+
222
+ # Initialize the environment variables
223
+ NOTION_BASE_URL = 'https://api.notion.com'
224
+ NOTION_DATABASE_ID = ENV.fetch('PTO_NOTION_DATABASE_ID')
225
+ NOTION_SECRET = ENV.fetch('PTO_NOTION_SECRET')
226
+ DISCORD_WEBHOOK = ENV.fetch('PTO_DISCORD_WEBHOOK')
227
+ DISCORD_BOT_NAME = ENV.fetch('PTO_DISCORD_BOT_NAME')
228
+
229
+ module Notifier
230
+ # Service description
231
+ class UseCaseName
232
+ def self.notify(*)
233
+ options = { fetch_options:, dispatch_options: }
234
+
235
+ begin
236
+ use_case = UseCases.use_case_build_function(options)
237
+
238
+ use_case.perform
239
+ rescue StandardError => e
240
+ { body: { message: e.message } }
241
+ end
242
+ end
243
+
244
+ def self.fetch_options
245
+ {
246
+ base_url: NOTION_BASE_URL,
247
+ database_id: NOTION_DATABASE_ID,
248
+ secret: NOTION_SECRET,
249
+ filter: {
250
+ filter: { "and": fetch_filter }
251
+ }
252
+ }
253
+ end
254
+
255
+ def self.fetch_filter
256
+ today = Common::TimeZone.set_colombia_time_zone.strftime('%F').to_s
257
+
258
+ [
259
+ { property: 'Desde?', date: { on_or_before: today } },
260
+ { property: 'Hasta?', date: { on_or_after: today } }
261
+ ]
262
+ end
263
+
264
+ def self.dispatch_options
265
+ {
266
+ webhook: DISCORD_WEBHOOK,
267
+ name: DISCORD_BOT_NAME
268
+ }
269
+ end
270
+
271
+ def self.format_options
272
+ {
273
+ template: ':beach: individual_name is on PTO'
274
+ }
275
+ end
276
+ end
277
+ end
278
+ ```
279
+
280
+ #### Configure the lambda
281
+ In the `serverless.yml` file, add a new instance in the `functions` block with this structure:
282
+
283
+ ```bash
284
+ functions:
285
+ ptoNotification:
286
+ handler: src/lambdas/pto_notification.Notifier::PTO.notify
287
+ environment:
288
+ PTO_NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.PTO_NOTION_DATABASE_ID}
289
+ PTO_NOTION_SECRET: ${file(./env.yml):${env:STAGE}.PTO_NOTION_SECRET}
290
+ PTO_DISCORD_WEBHOOK: ${file(./env.yml):${env:STAGE}.PTO_DISCORD_WEBHOOK}
291
+ PTO_DISCORD_BOT_NAME: ${file(./env.yml):${env:STAGE}.DISCORD_BOT_NAME}
292
+ events:
293
+ - schedule:
294
+ name: pto-daily-notification
295
+ description: "Notify every 24 hours at 8:30 a.m (UTC-5) from monday to friday"
296
+ rate: cron(${file(./env.yml):${env:STAGE}.PTO_SCHEDULER})
297
+ enabled: true
298
+ ```
299
+
300
+ #### Deploying
301
+
302
+ Configure the AWS keys:
303
+
304
+ ```bash
305
+ serverless config credentials --provider aws --key YOUR_KEY --secret YOUR_SECRET
306
+ ```
307
+
308
+ Deploy the project:
309
+ ```bash
310
+ STAGE=prod sls deploy --verbose
311
+ ```
153
312
 
154
313
  ## Development
155
314
 
data/SECURITY.md ADDED
@@ -0,0 +1,13 @@
1
+ # Security Policy
2
+
3
+ ## Reporting Security Issues
4
+
5
+ To report a security issue, you can either:
6
+ - Privately report a vulnerability through repository's Security tab by clicking "Report a vulnerability".
7
+ - Email us at [oss@kommit.co](mailto:oss@kommit.co).
8
+
9
+ Please, make sure to provide a description of the issue, the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.
10
+
11
+ ## Responsible Disclosure
12
+
13
+ If the issue is confirmed as a vulnerability, we will open a Security Advisory and acknowledge your contributions as part of it.
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dispatcher
4
- module Exceptions
5
- module Discord
4
+ module Discord
5
+ module Exceptions
6
6
  ##
7
7
  # Domain specific representation when an invalid Discord webhook token is provided to Discord.
8
8
  #
@@ -41,7 +41,7 @@ module Dispatcher
41
41
  def validate_response(response)
42
42
  case response.code
43
43
  when 50_027
44
- raise Exceptions::Discord::InvalidWebookToken, response.message
44
+ raise Discord::Exceptions::InvalidWebookToken, response.message
45
45
  else
46
46
  response
47
47
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dispatcher
4
+ module Slack
5
+ module Exceptions
6
+ ##
7
+ # Domain specific representation when an invalid webhook token is provided to Slack.
8
+ #
9
+ class InvalidWebookToken < StandardError
10
+ def initialize(message = "The provided Webhook token is invalid.")
11
+ super(message)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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 Slack
9
+ ##
10
+ # This class is an implementation of the Dispatcher::Base interface, specifically designed
11
+ # for dispatching messages to Slack.
12
+ #
13
+ class Implementation < Base
14
+ # Implements the dispatching logic for the Slack use case. It sends a POST request to
15
+ # the Slack webhook with the specified payload.
16
+ #
17
+ # <br>
18
+ # <b>Params:</b>
19
+ # * <tt>String</tt> payload: Payload to be dispatched to slack.
20
+ # <br>
21
+ # <b>raises</b> <tt>Exceptions::Slack::InvalidWebookToken</tt> if the provided webhook token is invalid.
22
+ #
23
+ # <br>
24
+ # <b>returns</b> <tt>Dispatcher::Slack::Types::Response</tt>
25
+ #
26
+ def dispatch(payload)
27
+ body = {
28
+ username: name,
29
+ text: payload
30
+ }.to_json
31
+
32
+ response = HTTParty.post(webhook, { body: body, headers: { "Content-Type" => "application/json" } })
33
+
34
+ slack_response = Dispatcher::Discord::Types::Response.new(response)
35
+
36
+ validate_response(slack_response)
37
+ end
38
+
39
+ private
40
+
41
+ def validate_response(response)
42
+ case response.http_code
43
+ when 403
44
+ raise Dispatcher::Slack::Exceptions::InvalidWebookToken, response.message
45
+ else
46
+ response
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dispatcher
4
+ module Slack
5
+ module Types
6
+ ##
7
+ # Represents a response received from Slack. It encapsulates essential information about the response,
8
+ # providing a structured way to handle and analyze Slack server responses.
9
+ #
10
+ class Response
11
+ attr_reader :body, :http_code, :message
12
+
13
+ def initialize(response)
14
+ @http_code = response.code
15
+ @message = response.message
16
+ @body = response.body
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -8,6 +8,8 @@ module Domain
8
8
  class Birthday
9
9
  attr_reader :individual_name, :birth_date
10
10
 
11
+ ATTRIBUTES = %w[individual_name birth_date].freeze
12
+
11
13
  # Initializes a Domain::Birthday instance with the specified individual name, and date of birth.
12
14
  #
13
15
  # <br>
@@ -9,6 +9,8 @@ module Domain
9
9
  class Pto
10
10
  attr_reader :individual_name, :start_date, :end_date
11
11
 
12
+ ATTRIBUTES = %w[individual_name start_date end_date].freeze
13
+
12
14
  # Initializes a Domain::Pto instance with the specified individual name, start date, and end date.
13
15
  #
14
16
  # <br>
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Domain
4
+ ##
5
+ # The Domain::WorkItemsLimit class provides a domain-specific representation of a Work Item object.
6
+ # It encapsulates information about a work items limit, including the domain, total, and wip_limit.
7
+ #
8
+ class WorkItemsLimit
9
+ attr_reader :domain, :total, :wip_limit
10
+
11
+ ATTRIBUTES = %w[domain total wip_limit].freeze
12
+
13
+ # Initializes a Domain::WorkItemsLimit instance with the specified domain, total and wip_limit.
14
+ #
15
+ # <br>
16
+ # <b>Params:</b>
17
+ # * <tt>String</tt> 'domain' responsible domain of the work items.
18
+ # * <tt>String</tt> 'total' total 'in progress' work items.
19
+ # * <tt>String</tt> 'wip_limit' maximum 'in progress' work items for the domain
20
+ #
21
+ def initialize(domain, total)
22
+ @domain = domain
23
+ @total = total
24
+ @wip_limit = domain_wip_limit(domain)
25
+ end
26
+
27
+ private
28
+
29
+ def domain_wip_limit(domain)
30
+ case domain
31
+ when "kommit.ops" then 5
32
+ when "kommit.sales" then 3
33
+ when "kommit.marketing" then 4
34
+ when "kommit.engineering" then 12
35
+ else 6
36
+ end
37
+ end
38
+ end
39
+ end
@@ -26,5 +26,18 @@ module Fetcher
26
26
  def fetch
27
27
  raise Domain::Exceptions::FunctionNotImplemented
28
28
  end
29
+
30
+ protected
31
+
32
+ # A method meant to execute the fetch request, retrieven the required data
33
+ # from an specific filter configuration depending on the use case implementation.
34
+ # Must be overridden by subclasses, with specific logic based on the use case.
35
+ #
36
+ # <br>
37
+ # <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
38
+ #
39
+ def execute
40
+ raise Domain::Exceptions::FunctionNotImplemented
41
+ end
29
42
  end
30
43
  end
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "httparty"
4
- require "date"
5
4
 
6
5
  require_relative "../base"
7
6
  require_relative "./exceptions/invalid_api_key"
8
7
  require_relative "./exceptions/invalid_database_id"
9
8
  require_relative "./types/response"
9
+ require_relative "./helper"
10
10
 
11
11
  module Fetcher
12
12
  module Notion
13
13
  ##
14
14
  # This class is an implementation of the Fetcher::Base interface, specifically designed
15
- # for fetching Paid Time Off (PTO) data from Notion.
15
+ # for fetching data from Notion.
16
16
  #
17
- class Pto < Base
18
- # Implements the data fetching logic for PTO's data from Notion. It sends a POST
17
+ class Base < Fetcher::Base
18
+ NOTION_BASE_URL = "https://api.notion.com"
19
+
20
+ protected
21
+
22
+ # Implements the data fetching logic for data from Notion. It sends a POST
19
23
  # request to the Notion API to query the specified database and returns a validated response.
20
24
  #
21
25
  # <br>
@@ -24,10 +28,10 @@ module Fetcher
24
28
  # <b>raises</b> <tt>Exceptions::Notion::InvalidDatabaseId</tt> if the Database id provided is incorrect
25
29
  # or invalid.
26
30
  #
27
- def fetch
28
- url = "#{config[:base_url]}/v1/databases/#{config[:database_id]}/query"
31
+ def execute(filter)
32
+ url = "#{NOTION_BASE_URL}/v1/databases/#{config[:database_id]}/query"
29
33
 
30
- httparty_response = HTTParty.post(url, { body: config[:filter].to_json, headers: headers })
34
+ httparty_response = HTTParty.post(url, { body: filter.to_json, headers: headers })
31
35
 
32
36
  notion_response = Fetcher::Notion::Types::Response.new(httparty_response)
33
37
 
@@ -13,7 +13,7 @@ module Fetcher
13
13
  if response["results"].nil?
14
14
  @status_code = response["status"]
15
15
  @message = response["message"]
16
- @results = nil
16
+ @results = []
17
17
  else
18
18
  @status_code = 200
19
19
  @message = "success"
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base"
4
+
5
+ module Fetcher
6
+ module Notion
7
+ ##
8
+ # This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
9
+ # for fetching next week birthdays data from Notion.
10
+ #
11
+ class BirthdayNextWeek < Notion::Base
12
+ DAYS_BEFORE_NOTIFY = 8
13
+
14
+ # Implements the data fetching filter for next week Birthdays data from Notion.
15
+ #
16
+ def fetch
17
+ filter = {
18
+ filter: {
19
+ or: [
20
+ { property: "BD_this_year", date: { equals: eight_days_from_now } }
21
+ ]
22
+ }
23
+ }
24
+
25
+ execute(filter)
26
+ end
27
+
28
+ private
29
+
30
+ def eight_days_from_now
31
+ date = Time.now.utc + days_in_second(DAYS_BEFORE_NOTIFY)
32
+
33
+ date.utc.strftime("%F").to_s
34
+ end
35
+
36
+ def days_in_second(days)
37
+ days * 24 * 60 * 60
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base"
4
+
5
+ module Fetcher
6
+ module Notion
7
+ ##
8
+ # This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
9
+ # for fetching birthday data from Notion.
10
+ #
11
+ class BirthdayToday < Notion::Base
12
+ # Implements the data fetching filter for todays Birthdays data from Notion.
13
+ #
14
+ def fetch
15
+ today = Time.now.utc.strftime("%F").to_s
16
+
17
+ filter = {
18
+ filter: {
19
+ or: [
20
+ { property: "BD_this_year", date: { equals: today } }
21
+ ]
22
+ }
23
+ }
24
+
25
+ execute(filter)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base"
4
+
5
+ module Fetcher
6
+ module Notion
7
+ ##
8
+ # This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
9
+ # for fetching next week Paid Time Off (PTO) data from Notion.
10
+ #
11
+ class PtoNextWeek < Notion::Base
12
+ # Implements the data fetching filter for next week PTO's data from Notion.
13
+ #
14
+ def fetch
15
+ filter = build_filter
16
+
17
+ execute(filter)
18
+ end
19
+
20
+ private
21
+
22
+ def next_week_dates
23
+ monday = next_week_monday
24
+ sunday = monday + 6
25
+
26
+ [monday, sunday]
27
+ end
28
+
29
+ def next_week_monday
30
+ today = Date.today
31
+ week_day = today.wday
32
+
33
+ days = week_day.zero? ? 1 : 8 - week_day
34
+
35
+ today + days
36
+ end
37
+
38
+ def build_filter
39
+ monday, sunday = next_week_dates
40
+
41
+ {
42
+ filter: {
43
+ or: [
44
+ belong_next_week("Desde?", monday, sunday),
45
+ belong_next_week("Hasta?", monday, sunday),
46
+ cover_next_week(monday, sunday)
47
+ ]
48
+ }
49
+ }
50
+ end
51
+
52
+ def belong_next_week(property, after_day, before_day)
53
+ {
54
+ and: [
55
+ { property: property, date: { on_or_after: after_day } },
56
+ { property: property, date: { on_or_before: before_day } }
57
+ ]
58
+ }
59
+ end
60
+
61
+ def cover_next_week(monday, sunday)
62
+ {
63
+ and: [
64
+ { property: "Hasta?", date: { on_or_after: sunday } },
65
+ { property: "Desde?", date: { on_or_before: monday } }
66
+ ]
67
+ }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base"
4
+
5
+ module Fetcher
6
+ module Notion
7
+ ##
8
+ # This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
9
+ # for fetching Paid Time Off (PTO) data from Notion.
10
+ #
11
+ class PtoToday < Notion::Base
12
+ # Implements the data fetching filter for todays PTO's data from Notion.
13
+ #
14
+ def fetch
15
+ today = Time.now.utc.strftime("%F").to_s
16
+
17
+ filter = {
18
+ filter: {
19
+ "and": [
20
+ { property: "Desde?", date: { on_or_before: today } },
21
+ { property: "Hasta?", date: { on_or_after: today } }
22
+ ]
23
+ }
24
+ }
25
+
26
+ execute(filter)
27
+ end
28
+ end
29
+ end
30
+ end