feature/IO-3060-Realtime-Notifications- Checkpoint

This commit is contained in:
Dave Richer
2025-01-13 07:27:10 -08:00
parent 3bc6504ae6
commit 8a0916a47f
25 changed files with 188 additions and 69 deletions

View File

@@ -683,6 +683,31 @@
- exported: - exported:
_eq: false _eq: false
event_triggers: event_triggers:
- name: notifications_bills
definition:
enable_manual: false
insert:
columns: '*'
retry_conf:
interval_sec: 10
num_retries: 0
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
body:
action: transform
template: |-
{
"success": true
}
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/job/events/handleBillsChange'
version: 2
- name: os_bills - name: os_bills
definition: definition:
delete: delete:
@@ -2813,7 +2838,7 @@
object_relationships: object_relationships:
- name: job - name: job
using: using:
foreign_key_constraint_on: job_id foreign_key_constraint_on: jobid
- name: user - name: user
using: using:
foreign_key_constraint_on: user_email foreign_key_constraint_on: user_email
@@ -2832,7 +2857,7 @@
- user_email - user_email
- created_at - created_at
- id - id
- job_id - jobid
comment: "" comment: ""
select_permissions: select_permissions:
- role: user - role: user
@@ -2841,7 +2866,7 @@
- user_email - user_email
- created_at - created_at
- id - id
- job_id - jobid
filter: filter:
user: user:
_and: _and:
@@ -2858,7 +2883,7 @@
- user_email - user_email
- created_at - created_at
- id - id
- job_id - jobid
filter: filter:
user: user:
_and: _and:
@@ -4402,6 +4427,58 @@
template_engine: Kriti template_engine: Kriti
url: '{{$base_url}}/record-handler/arms' url: '{{$base_url}}/record-handler/arms'
version: 2 version: 2
- name: notifications_jobs
definition:
enable_manual: false
insert:
columns: '*'
update:
columns:
- queued_for_parts
- employee_prep
- clm_total
- towin
- employee_body
- converted
- scheduled_in
- scheduled_completion
- scheduled_delivery
- actual_delivery
- actual_completion
- alt_transport
- date_exported
- status
- employee_csr
- actual_in
- deliverchecklist
- comment
- job_totals
- employee_refinish
- inproduction
- production_vars
- intakechecklist
- cieca_ttl
- date_invoiced
retry_conf:
interval_sec: 10
num_retries: 0
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
body:
action: transform
template: |-
{
"success": true
}
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/job/events/handleJobsChange'
version: 2
- name: os_jobs - name: os_jobs
definition: definition:
delete: delete:
@@ -4751,46 +4828,21 @@
object_relationships: object_relationships:
- name: association - name: association
using: using:
foreign_key_constraint_on: association_id foreign_key_constraint_on: associationid
- name: job - name: job
using: using:
foreign_key_constraint_on: job_id foreign_key_constraint_on: jobid
insert_permissions:
- role: user
permission:
check:
association:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
columns:
- association_id
- created_at
- fcm_data
- fcm_message
- fcm_title
- id
- job_id
- meta
- read
- ui_translation_meta
- ui_translation_string
- updated_at
comment: ""
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- association_id - associationid
- created_at - created_at
- fcm_data - fcm_data
- fcm_message - fcm_message
- fcm_title - fcm_title
- id - id
- job_id - jobid
- meta - meta
- read - read
- ui_translation_meta - ui_translation_meta
@@ -4809,18 +4861,8 @@
- role: user - role: user
permission: permission:
columns: columns:
- association_id
- created_at
- fcm_data
- fcm_message
- fcm_title
- id
- job_id
- meta - meta
- read - read
- ui_translation_meta
- ui_translation_string
- updated_at
filter: filter:
association: association:
_and: _and:
@@ -5071,6 +5113,32 @@
- active: - active:
_eq: true _eq: true
check: null check: null
event_triggers:
- name: notifications_parts_dispatch
definition:
enable_manual: false
insert:
columns: '*'
retry_conf:
interval_sec: 10
num_retries: 0
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
body:
action: transform
template: |-
{
"success": true
}
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/job/events/handlePartsDispatchChange'
version: 2
- table: - table:
name: parts_dispatch_lines name: parts_dispatch_lines
schema: public schema: public
@@ -6049,6 +6117,8 @@
update: update:
columns: columns:
- assigned_to - assigned_to
- completed
- description
retry_conf: retry_conf:
interval_sec: 10 interval_sec: 10
num_retries: 3 num_retries: 3
@@ -6203,8 +6273,6 @@
enable_manual: false enable_manual: false
insert: insert:
columns: '*' columns: '*'
update:
columns: '*'
retry_conf: retry_conf:
interval_sec: 10 interval_sec: 10
num_retries: 0 num_retries: 0
@@ -6214,10 +6282,16 @@
- name: event-secret - name: event-secret
value_from_env: EVENT_SECRET value_from_env: EVENT_SECRET
request_transform: request_transform:
body:
action: transform
template: |-
{
"success": true
}
method: POST method: POST
query_params: {} query_params: {}
template_engine: Kriti template_engine: Kriti
url: '{{$base_url}}/events/handleTimeTicketsChange' url: '{{$base_url}}/job/events/handleTimeTicketsChange'
version: 2 version: 2
- table: - table:
name: transitions name: transitions

View File

@@ -0,0 +1,3 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- update associations set notification_settings = '{}';

View File

@@ -0,0 +1 @@
update associations set notification_settings = '{}';

View File

@@ -0,0 +1 @@
alter table "public"."associations" alter column "notification_settings" drop not null;

View File

@@ -0,0 +1 @@
alter table "public"."associations" alter column "notification_settings" set not null;

View File

@@ -0,0 +1,5 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- update notifications set meta = '{}';
-- update notifications set fcm_data = '{}';
-- update notifications set ui_translation_meta = '{}';

View File

@@ -0,0 +1,3 @@
update notifications set meta = '{}';
update notifications set fcm_data = '{}';
update notifications set ui_translation_meta = '{}';

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "meta" drop not null;
ALTER TABLE "public"."notifications" ALTER COLUMN "meta" drop default;

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "meta" set default jsonb_build_object();
alter table "public"."notifications" alter column "meta" set not null;

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "fcm_data" drop not null;
ALTER TABLE "public"."notifications" ALTER COLUMN "fcm_data" drop default;

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "fcm_data" set default jsonb_build_object();
alter table "public"."notifications" alter column "fcm_data" set not null;

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "ui_translation_meta" drop not null;
ALTER TABLE "public"."notifications" ALTER COLUMN "ui_translation_meta" drop default;

View File

@@ -0,0 +1,2 @@
alter table "public"."notifications" alter column "ui_translation_meta" set default jsonb_build_object();
alter table "public"."notifications" alter column "ui_translation_meta" set not null;

View File

@@ -0,0 +1 @@
alter table "public"."notifications" rename column "jobid" to "job_id";

View File

@@ -0,0 +1 @@
alter table "public"."notifications" rename column "job_id" to "jobid";

View File

@@ -0,0 +1 @@
alter table "public"."notifications" rename column "associationid" to "association_id";

View File

@@ -0,0 +1 @@
alter table "public"."notifications" rename column "association_id" to "associationid";

View File

@@ -0,0 +1 @@
alter table "public"."job_watchers" rename column "jobid" to "job_id";

View File

@@ -0,0 +1 @@
alter table "public"."job_watchers" rename column "job_id" to "jobid";

View File

@@ -142,27 +142,38 @@ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInst
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
const taskAssignedEmail = async (req, res) => { const taskAssignedEmail = async (req, res) => {
// We have no event Data, bail // 1. Check if we have new task data in the event body.
if (!req?.body?.event?.data?.new) { if (!req?.body?.event?.data?.new) {
return res.status(400).json({ message: "No data in the event body" }); return res.status(400).json({ message: "No data in the event body" });
} }
const { new: newTask } = req.body.event.data; const { new: newTask, old: oldTask } = req.body.event.data;
// This is not a new task, but a reassignment. // TODO: THIS IS HERE BECAUSE THE HANDLER NOW DOES 3 FIELDS, WILL NEED TO BE BUILT ON FOR NOTIFICATIONS
const dirty = req.body.event.data?.old && req.body.event.data?.old?.assigned_to; if (oldTask && oldTask.assigned_to === newTask.assigned_to) {
return res.status(200).json({ success: true, message: "assigned_to not changed" });
}
//Query to get the employee assigned currently. // 3. If we made it here, assigned_to changed (or oldTask is missing),
// so we continue with the rest of the logic.
// Query to get the task with bodyshop data, assigned employee data, etc.
const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, { const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, {
id: newTask.id id: newTask.id
}); });
// Format date/time with the correct timezone
const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a");
// This determines if it was re-assigned (old task exists and had an assigned_to).
const dirty = oldTask && oldTask.assigned_to;
sendMail( sendMail(
"assigned", "assigned",
tasks_by_pk.assigned_to_employee.user_email, tasks_by_pk.assigned_to_employee.user_email,
`A ${formatPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`, `A ${formatPriority(newTask.priority)} priority task has been ${
dirty ? "reassigned" : "created"
} for you - ${newTask.title}`,
generateEmailTemplate( generateEmailTemplate(
generateTemplateArgs( generateTemplateArgs(
newTask.title, newTask.title,
@@ -181,8 +192,8 @@ const taskAssignedEmail = async (req, res) => {
tasks_by_pk.bodyshop.convenient_company tasks_by_pk.bodyshop.convenient_company
); );
// We return success regardless because we don't want to block the event trigger. // Return success so we don't block the event trigger.
res.status(200).json({ success: true }); return res.status(200).json({ success: true });
}; };
/** /**

View File

@@ -1,5 +0,0 @@
const handleJobChange = (req, res) => {
return res.status(200).json({ message: "Job change handled." });
};
module.exports = handleJobChange;

View File

@@ -0,0 +1,5 @@
const handleJobsChange = (req, res) => {
return res.status(200).json({ message: "Jobs change handled." });
};
module.exports = handleJobsChange;

View File

@@ -1,5 +0,0 @@
const handleBillChange = (req, res) => {
return res.status(200).json({ message: "Bill change handled." });
};
module.exports = handleBillChange;

View File

@@ -0,0 +1,5 @@
const handleBillsChange = (req, res) => {
return res.status(200).json({ message: "Bills change handled." });
};
module.exports = handleBillsChange;

View File

@@ -2,11 +2,13 @@ const express = require("express");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler"); const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler");
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware"); const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
const handleJobChange = require("../notifications/eventHandlers/handeJobChange");
const handleBillChange = require("../notifications/eventHandlers/handleBillChange");
const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange"); const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange");
const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange"); const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange");
const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange"); const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange");
const handleTimeTicketsChange = require("../notifications/eventHandlers/handleTimeTicketsChange");
const handleJobsChange = require("../notifications/eventHandlers/handeJobsChange");
const handleBillsChange = require("../notifications/eventHandlers/handleBillsChange");
const router = express.Router(); const router = express.Router();
@@ -16,11 +18,11 @@ router.post("/unsubscribe", validateFirebaseIdTokenMiddleware, unsubscribe);
router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification); router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification);
// Hasura Entry points for creating notifications // Hasura Entry points for creating notifications
router.post("/events/handleJobChange", eventAuthorizationMiddleware, handleJobChange); router.post("/events/handleJobsChange", eventAuthorizationMiddleware, handleJobsChange);
router.post("/events/handleBillChange", eventAuthorizationMiddleware, handleBillChange); router.post("/events/handleBillsChange", eventAuthorizationMiddleware, handleBillsChange);
router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange); router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange);
router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange); router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange);
router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange); router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange);
router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTasksChange); router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTimeTicketsChange);
module.exports = router; module.exports = router;