bns 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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