Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tolk 1747 varsling av inkommende sms #2061

Merged
merged 16 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2cc88c8
Begynnelse på sms notifikasjonsklasse
AndreMarthinsen Sep 5, 2024
0d0dae2
Oppdaterer getQueueIdsFromSenders og test til å fungere som forventet
AndreMarthinsen Sep 8, 2024
5a9adf4
Legger til trigger for håndtering av inkommende sms
AndreMarthinsen Sep 9, 2024
c1620a9
FIkser notifikasjoner til å bruke navn og ikke personnummer, legger t…
AndreMarthinsen Sep 9, 2024
edba861
Merge branch 'TOLK-1747-varsling-av-inkommende-sms' of https://github…
AndreMarthinsen Sep 9, 2024
98ce01b
Fjerner - <Navn> fra notification body
AndreMarthinsen Sep 9, 2024
0d1ce40
Merge branch 'TOLK-1747-varsling-av-inkommende-sms' of https://github…
AndreMarthinsen Sep 9, 2024
e0e23bf
Legger til håndtering av flere sms fra samme bruker og bedre testing
AndreMarthinsen Sep 10, 2024
0f9fd9a
Merge branch 'master' into TOLK-1747-varsling-av-inkommende-sms
AndreMarthinsen Sep 10, 2024
368c476
Legger til tidlig slutt på funksjon ved null gyldige SMS
AndreMarthinsen Sep 10, 2024
2a678ae
Merge branch 'TOLK-1747-varsling-av-inkommende-sms' of https://github…
AndreMarthinsen Sep 10, 2024
0227c10
Merge branch 'master' into TOLK-1747-varsling-av-inkommende-sms
AndreMarthinsen Sep 10, 2024
ee49d6e
Spørre på manglende felter på sms
EdvardBrekke Sep 11, 2024
6290949
Fikser manglende insert av SMS, formatering.
AndreMarthinsen Sep 11, 2024
8c186da
Merge branch 'master' into TOLK-1747-varsling-av-inkommende-sms
AndreMarthinsen Sep 11, 2024
95725fc
Merge branch 'master' into TOLK-1747-varsling-av-inkommende-sms
AndreMarthinsen Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions force-app/main/notification/classes/HOT_IncomingSMSNotification.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
public with sharing class HOT_IncomingSMSNotification {
private static final Integer MAX_NOTIFICATION_BODY_CHARACTERS = 320;
public static final String DISPATCH_INCOMING_NUMBER = '41716090';

@TestVisible
private class NotificationContext {
public SMS__c sms;
public Set<String> recipients;
public Messaging.CustomNotification notification;
}

public static Integer notifyDispatchersOnIncomingSMS(List<SMS__c> messages) {
List<NotificationContext> contexts = prepareContextsForValidSMS(messages);

for (NotificationContext context : contexts) {
HOT_NotificationHandler.sendNotification(context.notification, context.recipients, (SObject) context.sms);
}
return contexts.size();
}

@TestVisible
private static List<NotificationContext> prepareContextsForValidSMS(List<SMS__c> messages) {
List<NotificationContext> contextList = new List<NotificationContext>();

Map<Id, List<NotificationContext>> accountIdToContexts = new Map<Id, List<NotificationContext>>();
Set<Id> smsIds = new Set<Id>();
for (SMS__c sms : messages) {
smsIds.add(sms.Id);
}

Map<Id, SMS__c> smsRecords = new Map<Id, SMS__c>(
[
SELECT Account__c, Domain__c, Type__c, Recipient__c, Message__c
FROM SMS__c
WHERE Id IN :smsIds
]
);

for (SMS__c sms : messages) {
if (shouldNotifyDispatchersAboutSMS(smsRecords.get(sms.Id))) {
NotificationContext context = new NotificationContext();
context.sms = smsRecords.get(sms.Id);
List<NotificationContext> contexts = accountIdToContexts.get(smsRecords.get(sms.Id).Account__c);
if (contexts == null) {
contexts = new List<NotificationContext>();
}
contexts.add(context);
accountIdToContexts.put(smsRecords.get(sms.Id).Account__c, contexts);
}
}

if (accountIdToContexts.isEmpty()) {
return contextList;
}

List<Person__c> senders = [
SELECT Id, CRM_Account__c, INT_RegionNumber__c, INT_MunicipalityNumber__c, CRM_FullName__c
FROM Person__c
WHERE CRM_Account__c IN :accountIdToContexts.keySet()
];
List<Id> queueIds = getQueueIdsFromSenders(senders);
Map<Id, Id> groupByQueue = HOT_NotificationHandler.getGroupIdByQueueId(queueIds);

CustomNotificationType notificationType = [
SELECT Id, DeveloperName
FROM CustomNotificationType
WHERE DeveloperName = 'HOT_NotifyDispatcher'
];

Integer index = 0;
for (Person__c sender : senders) {
List<NotificationContext> contexts = accountIdToContexts.get(sender.CRM_Account__c);
for (NotificationContext context : contexts) {
context.recipients = new Set<String>{ (String) groupByQueue.get(queueIds[index]) };

String title = 'Melding fra ' + sender.CRM_FullName__c;
String body = context.sms.Message__c.length() > MAX_NOTIFICATION_BODY_CHARACTERS
? context.sms.Message__c.substring(0, MAX_NOTIFICATION_BODY_CHARACTERS - 3) + '...'
: context.sms.Message__c;

context.notification = HOT_NotificationHandler.prepareNotification(
title,
body,
notificationType.Id,
context.sms.Id
);
contextList.add(context);
}
index++;
}

return contextList;
}

@TestVisible
private static List<Id> getQueueIdsFromSenders(List<Person__c> senders) {
List<SobjectWrapper> wrappers = new List<SobjectWrapper>();
Integer senderIndex = 0;

for (Person__c sender : senders) {
SobjectWrapper wrapper = new SobjectWrapper(
senderIndex,
sender.INT_MunicipalityNumber__c,
sender.INT_RegionNumber__c
);
wrapper.confidential = 'Ugradert';
wrappers.add(wrapper);
senderIndex++;
}

Map<Integer, ApexSharingRuleWrapper> indexToSharingRule = RecordOwnerService.getQueuesAndNavUnits(
wrappers,
'Person__c'
);

senderIndex = 0;
List<Id> queueIds = new List<Id>();

Group adminGroup = [SELECT Id FROM Group WHERE Type = 'Queue' AND DeveloperName = 'HOT_Tolk_Admin' LIMIT 1];
for (Person__c sender : senders) {
ApexSharingRuleWrapper rule = indexToSharingRule.get(senderIndex);
if (rule != null) {
queueIds.add(rule.queueId);
} else {
queueIds.add(adminGroup.Id);
}
senderIndex++;
}

return queueIds;
}

@TestVisible
private static Boolean shouldNotifyDispatchersAboutSMS(SMS__c sms) {
return sms.Account__c != null &&
sms.Domain__c == 'HOT' &&
sms.Type__c == 'Incoming SMS' &&
sms.Recipient__c == DISPATCH_INCOMING_NUMBER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>61.0</apiVersion>
<status>Active</status>
</ApexClass>
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
@isTest
private class HOT_IncomingSMSNotificationTest {
static List<String> userNames = new List<String>{ 'A_User', 'B_User', 'C_User' };
static List<String> userMunicipalities = new List<String>{ '3216', '5001', '3407' };
static List<String> userRegionNumbers = new List<String>{ '03', '50', '34' };

@TestSetup
static void makeData() {
myTriggers.disable(PersonHandler.class);
myTriggers.disable(PersonAccessHandler.class);
myTriggers.disable(CommunityUserUpdateHandler.class);
User navEmployee = TestDataFactory_Community.getUsers(1, 'System Administrator', true, true)[0];
System.runAs(navEmployee) {
Account account = new Account();
account.Name = 'Gunnar Gunn Gundersen';
insert account;

TestDataFactory.getPublicGroup('HMS Oslo', 'group_4703');
TestDataFactory.getQueue('HMS Oslo', 'queue_4703', 'Person__c');
TestDataFactory.getPublicGroup('HMS Trøndelag', 'group_4716');
TestDataFactory.getQueue('HMS Trøndelag', 'queue_4716', 'Person__c');
TestDataFactory.getPublicGroup('HMS Innlandet', 'group_47003');
TestDataFactory.getQueue('HMS Innlandet', 'queue_47003', 'Person__c');

List<Account> personAccounts = TestDataFactory_Community.getPersonAccounts(userNames.size());
Map<String, Account> accountByLastName = new Map<String, Account>();
for (Account acc : personAccounts) {
accountByLastName.put(acc.LastName, acc);
}

Set<Id> personAccountIds = new Set<Id>();
List<Person__c> persons = [SELECT INT_LastName__c, CRM_Account__c, Name FROM Person__c];
Integer index = 0;
for (Person__c person : persons) {
person.CRM_Account__c = accountByLastName.get(person.INT_LastName__c).Id;
person.Name = userNames[index];
person.INT_MunicipalityNumber__c = userMunicipalities[index];
person.INT_RegionNumber__c = userRegionNumbers[index];
index++;
personAccountIds.add(person.CRM_Account__c);
}
update persons;
List<User> users = TestDataFactory_Community.getEmployerCommunityUser(
personAccountIds,
'Trial Customer Portal User',
true
);
}
}

@isTest
static void getQueueIdsFromSendersTest() {
List<Person__c> persons = [
SELECT Id, Name, CRM_Account__c, INT_MunicipalityNumber__c, INT_RegionNumber__c
FROM Person__c
WHERE Name IN :userNames
ORDER BY Name
];

Test.startTest();
List<Id> groupIds = HOT_IncomingSMSNotification.getQueueIdsFromSenders(persons);
Test.stopTest();

Id osloQueueId = [SELECT Id FROM Group WHERE DeveloperName = 'queue_4703' LIMIT 1].Id;
System.assertEquals(osloQueueId, groupIds[0], 'Person 0 should belong to Oslo region');

Id trondelagQueueId = [SELECT Id FROM Group WHERE DeveloperName = 'queue_4716' LIMIT 1].Id;
System.assertEquals(trondelagQueueId, groupIds[1], 'Person 1 should belong to trondelag region');

Id innlandetQueueId = [SELECT Id FROM Group WHERE DeveloperName = 'queue_47003' LIMIT 1].Id;
System.assertEquals(innlandetQueueId, groupIds[2], 'Person 2 should belong to Innlandet region');
}

@isTest
static void shouldNotifyDispatchersAboutSMSTest() {
Account acc = [SELECT Id, Name FROM Account WHERE Name = 'Gunnar Gunn Gundersen'];
SMS__c testSMS = new SMS__c();
testSMS.Domain__c = 'HOT';
testSMS.Type__c = 'Incoming SMS';
testSMS.Recipient__c = HOT_IncomingSMSNotification.DISPATCH_INCOMING_NUMBER;
testSMS.Account__c = acc.Id;

insert testSMS;

Test.startTest();
Boolean shouldNotify = HOT_IncomingSMSNotification.shouldNotifyDispatchersAboutSMS(testSMS);
Test.stopTest();

System.assertEquals(true, shouldNotify, 'SMS should trigger notification of dispatchers');
}

@isTest
static void notifyDispatchersOnIncomingSMSTest() {
Person__c sender = [SELECT Id, CRM_Account__c, Name FROM Person__c WHERE Name = :userNames[0]];
SMS__c testSMS = new SMS__c();
testSMS.Domain__c = 'HOT';
testSMS.Message__c = 'Dette er en testmelding';
testSMS.Type__c = 'Incoming SMS';
testSMS.Recipient__c = HOT_IncomingSMSNotification.DISPATCH_INCOMING_NUMBER;
testSMS.Account__c = sender.CRM_Account__c;

SMS__c invalidSMS = new SMS__c();
invalidSMS.Domain__c = 'HOT';
invalidSMS.Type__c = 'Incoming SMS';
invalidSMS.Recipient__c = '2345';
invalidSMS.Account__c = null;

List<SMS__c> smsList = new List<SMS__c>{ testSMS, invalidSMS };
insert smsList;

Test.startTest();
Integer notificationsSent = HOT_IncomingSMSNotification.notifyDispatchersOnIncomingSMS(smsList);
Test.stopTest();

System.assertEquals(1, notificationsSent, 'should send only one notification');
}

@isTest
static void prepareContextsForValidSMSTest() {
Person__c sender = [SELECT Id, CRM_Account__c, Name FROM Person__c WHERE Name = :userNames[0]];
SMS__c testSMS = new SMS__c();
testSMS.Domain__c = 'HOT';
testSMS.Message__c = 'Dette er en testmelding';
testSMS.Type__c = 'Incoming SMS';
testSMS.Recipient__c = HOT_IncomingSMSNotification.DISPATCH_INCOMING_NUMBER;
testSMS.Account__c = sender.CRM_Account__c;

SMS__c secondSMS = new SMS__c();
secondSMS.Domain__c = 'HOT';
secondSMS.Message__c = 'En annen melding fra sammes ender';
secondSMS.Type__c = 'Incoming SMS';
secondSMS.Recipient__c = HOT_IncomingSMSNotification.DISPATCH_INCOMING_NUMBER;
secondSMS.Account__c = sender.CRM_Account__c;

SMS__c invalidSMS = new SMS__c();
invalidSMS.Domain__c = 'HOT';
invalidSMS.Type__c = 'Incoming SMS';
invalidSMS.Recipient__c = '2345';
invalidSMS.Account__c = null;

List<SMS__c> incomingSMS = new List<SMS__c>{ testSMS, secondSMS, invalidSMS };
insert incomingSMS;

Test.startTest();
List<HOT_IncomingSMSNotification.NotificationContext> contexts = HOT_IncomingSMSNotification.prepareContextsForValidSMS(
incomingSMS
);
Test.stopTest();

System.assertEquals(
2,
contexts.size(),
'should only return contects for valid sms messages, and should return contexts for both sms from same sender'
);
System.assert(
contexts[0].sms == testSMS && contexts[1].sms == secondSMS,
'Contexts should contain notificationContext with valid properties'
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>61.0</apiVersion>
<status>Active</status>
</ApexClass>
4 changes: 4 additions & 0 deletions force-app/main/triggers/classes/HOT_SMSHandler.cls
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ public without sharing class HOT_SMSHandler extends MyTriggers {
HOT_Utility.setArchiveAsOwner(SMSToChangeOwnerToArchive);
}
}

public override void onAfterInsert() {
HOT_IncomingSMSNotification.notifyDispatchersOnIncomingSMS((List<SMS__c>) records);
}
}
7 changes: 7 additions & 0 deletions force-app/main/triggers/classes/HOT_SMSHandlerTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ public with sharing class HOT_SMSHandlerTest {
SMS__c updatedSms = [SELECT Id, OwnerId FROM SMS__c WHERE Id = :sms.Id LIMIT 1];
System.assertEquals(archive.Id, updatedSms.OwnerId, 'The owner was not changed to "HOT Arkiv" as expected.');
}

@IsTest
static void notifyDispatchersOnInsert() {
SMS__c sms = HOT_TestDataFactory.createSms();
sms.Type__c = 'Incoming SMS';
insert sms;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<label>HOT SMS onAfterUpdate</label>
<protected>false</protected>
<values>
<field>Active__c</field>
<value xsi:type="xsd:boolean">true</value>
</values>
<values>
<field>ClassNamespacePrefix__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>Class__c</field>
<value xsi:type="xsd:string">HOT_SMSHandler</value>
</values>
<values>
<field>Description__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>Event__c</field>
<value xsi:type="xsd:string">AFTER_INSERT</value>
</values>
<values>
<field>IsBypassAllowed__c</field>
<value xsi:type="xsd:boolean">false</value>
</values>
<values>
<field>Order__c</field>
<value xsi:type="xsd:double">200.0</value>
</values>
<values>
<field>sObjectAPIName__c</field>
<value xsi:nil="true"/>
</values>
<values>
<field>sObject__c</field>
<value xsi:type="xsd:string">SMS__c</value>
</values>
</CustomMetadata>
Loading