Skip to content

Commit 5f509cb

Browse files
committed
Move application-monitoring from crm-platform-base
1 parent 3ff623f commit 5f509cb

File tree

55 files changed

+2599
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2599
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# feature-toggle
2+
3+
Denne pakken inneholder et rammeverk for feature toggling som kan brukes som det er eller extendes.
4+
5+
Datamodellen ligger i pakken [platform-data-model](src/platform-data-model/README.md)
6+
7+
| | |
8+
| ---- | --- |
9+
| Apex ||
10+
| LWC ||
11+
| Flow ||
12+
13+
## Hvordan ta i bruk
14+
15+
Avhengig av behov så kan man enten bruke `FeatureToggleBase` direkte eller extende den for å se om en feature er aktiv eller ikke.
16+
Denne kan brukes på to nivåer, individuell eller generell.
17+
18+
Feature toggle customMetadata innslag og customPermissions legges i de pakkene hvor de brukes.
19+
20+
![Bilde viser opprettelse av et nytt feature flag](../../../resources/img/feature-toggle_new_myFeatureFlag.png "Nytt Feature Flag")
21+
22+
**Generell:**
23+
Det man ønsker å enkelt kunne skru en feature på eller av. Opprett en custom metadata feature toggle og aktiver/deaktiver den ved å benytte Is Enabled (`Is_Enabled__c`) flagget.
24+
I koden sjekker man på `isFeatureEnabled(<developername til gitt feature>)`. Dette vil da gjelde for all logikk som kjører koden.
25+
26+
**Individuell/gruppe:**
27+
Her kan man velge å bruke enten `customPermission` eller en kombinasjon av `Feature_Flag__mdt` og `customPermission`.
28+
29+
Ved bruk av bare `customPermission` oppretter man en custom permission og legger den til et Permission Set som tildeles den eller de som trenger tilgangen.
30+
31+
Ved bruk av en kombinasjon så opprettes det et custom permission som tildeles via Permission Set slik som over. I tillegg opprettes det et `Feature_Flag__mdt` record hvor customPermission api navnet legges til i Required Custo Permission (`Required_Custom_Permission__c`). Is Enabled (`Is_Enabled__c`) flagget benyttes som vanlig til å toggle featuren av og på.
32+
33+
**Eksempel på direkte bruk:**
34+
35+
```java
36+
public with sharing MyClass {
37+
public MyClass() {
38+
...
39+
}
40+
41+
public Decimal getDiscount() {
42+
if(new FeatureToggleBase().isFeatureEnabled('improved-discount-calculation')) {
43+
return getDiscountBeta();
44+
}
45+
...
46+
return discount
47+
}
48+
49+
private Decimal getDiscountBeta() {
50+
...
51+
return discount;
52+
}
53+
54+
...
55+
}
56+
```
57+
58+
**Eksempel hvor man extender FeatureToggleBase:**
59+
60+
```java
61+
public with sharing MyClass extends FeatureToggleBase {
62+
public MyClass() {
63+
...
64+
}
65+
66+
public Decimal getDiscount() {
67+
if(isFeatureEnabled('improved-discount-calculation')) {
68+
return getDiscountBeta();
69+
}
70+
...
71+
return discount
72+
}
73+
74+
private Decimal getDiscountBeta() {
75+
...
76+
return discount;
77+
}
78+
79+
...
80+
}
81+
```
82+
83+
## Avhengigheter
84+
85+
- [platform-datamodel](src/platform-data-model/feature-flag-custom-metadata) - datamodell
86+
- [custom-metadata-dao](src/platform-utility/custom-metadata-dao) - Abstraksjon av custom metadata
87+
- [custom-permission-helper](src/platform-utility/custom-permission-helper) - Hjelpe klasse for å kunne mocke og validere custom permissions
88+
89+
```mermaid
90+
---
91+
title: Pakkeavhengigheter
92+
---
93+
graph TD
94+
feature-toggle --> platform-datamodel;
95+
feature-toggle --> custom-metadata-dao;
96+
feature-toggle --> custom-pemission-helper
97+
```
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
public class ApplicationLogPublisher {
2+
public static Application_Log_Setting__mdt testSetting;
3+
//Introduce setting to determine if messages should be published also for sandboxes
4+
private static Boolean isSandbox = [
5+
SELECT Id, isSandbox
6+
FROM Organization
7+
LIMIT 1
8+
]
9+
.isSandbox;
10+
11+
/**
12+
* @description: Performs a check on created application logs to see if any meet
13+
* the requirements for publishing to slack channels.
14+
* @author Stian Ruud Schikora | 02-23-2021
15+
* @param appLogs
16+
**/
17+
public static void publishLogs(List<Application_Log__c> appLogs) {
18+
Set<String> appDomains = new Set<String>();
19+
List<AppLog> logsToPublish = new List<AppLog>();
20+
21+
for (Application_Log__c log : appLogs) {
22+
appDomains.add(log.Application_Domain__c);
23+
}
24+
25+
List<Application_Log_Setting__mdt> logSettings = getLogSettings(
26+
appDomains
27+
);
28+
29+
for (Application_Log__c log : appLogs) {
30+
for (Application_Log_Setting__mdt setting : logSettings) {
31+
if (
32+
log.Application_Domain__c == setting.Application_Domain__c
33+
) {
34+
String hook = getHook(setting);
35+
if (
36+
getLogLevelOrdinal(log.Log_Level__c) >=
37+
getLogLevelOrdinal(setting.Minimum_Log_Level__c) &&
38+
setting.Category__c == log.Category__c
39+
) {
40+
if (
41+
Setting.Immediate_Post__c && String.isNotBlank(hook)
42+
) {
43+
logsToPublish.add(
44+
new AppLog(
45+
log,
46+
hook,
47+
Setting.Message_Template__c
48+
)
49+
);
50+
}
51+
}
52+
} else {
53+
continue;
54+
}
55+
}
56+
}
57+
58+
if (!logsToPublish.isEmpty()) {
59+
//If there are more logs to publish than the transactional callout limit, generate several future contexts
60+
if (logsToPublish.size() > Limits.getLimitCallouts()) {
61+
List<AppLog> listToPublish = new List<AppLog>();
62+
while (logsToPublish.size() > 0) {
63+
listToPublish.add(logsToPublish.remove(0));
64+
if (listToPublish.size() == Limits.getLimitCallouts()) {
65+
publishToSlack(JSON.serialize(listToPublish));
66+
listToPublish.clear();
67+
}
68+
}
69+
} else {
70+
publishToSlack(JSON.serialize(logsToPublish));
71+
}
72+
}
73+
}
74+
75+
@future(Callout=true)
76+
private static void publishToSlack(String jsonAppLogs) {
77+
List<AppLog> logs = (List<AppLog>) JSON.deserialize(
78+
jsonAppLogs,
79+
List<AppLog>.class
80+
);
81+
ApiController apiCtrl = new ApiController();
82+
for (AppLog log : logs) {
83+
apiCtrl.setEndpoint(log.slackHook);
84+
apiCtrl.setMethod('POST');
85+
apiCtrl.addHeader('Content-Type', 'application/json');
86+
87+
SlackMessage message = new SlackMessage(log.messageTemplate);
88+
String errorMessage =
89+
'Log level: ' +
90+
log.logRecord.Log_Level__c +
91+
'\n Source: ' +
92+
log.logRecord.Source_Class__c +
93+
'\n Link: ' +
94+
URL.getSalesforceBaseUrl().toExternalForm() +
95+
'/' +
96+
log.logRecord.Id +
97+
'\n Message: ' +
98+
log.logRecord.Log_Message__c;
99+
message.addSection(errorMessage);
100+
101+
apiCtrl.setBody(JSON.serialize(message));
102+
103+
apiCtrl.doCallout();
104+
}
105+
}
106+
107+
/**
108+
* @description: Queries the relevant log settings for the defined application domains
109+
* @author Stian Ruud Schikora | 02-23-2021
110+
* @param appDomains
111+
* @return List<Application_Log_Setting__mdt>
112+
**/
113+
private static List<Application_Log_Setting__mdt> getLogSettings(
114+
Set<String> appDomains
115+
) {
116+
if (Test.isRunningTest()) {
117+
return testSetting != null
118+
? new List<Application_Log_Setting__mdt>{ testSetting }
119+
: new List<Application_Log_Setting__mdt>();
120+
}
121+
return [
122+
SELECT
123+
Id,
124+
MasterLabel,
125+
Application_Domain__c,
126+
Slack_Hook__c,
127+
Slack_Hook_Sandbox__c,
128+
Message_Template__c,
129+
Immediate_Post__c,
130+
Minimum_Log_Level__c,
131+
Named_Credential__c,
132+
Category__c
133+
FROM Application_Log_Setting__mdt
134+
WHERE Application_Domain__c IN :appDomains
135+
];
136+
}
137+
138+
/**
139+
* @description: Returns the correct slack hook for production or sandbox.
140+
* @author Stian Ruud Schikora | 03-13-2021
141+
* @param logSetting
142+
* @return String
143+
**/
144+
private static String getHook(Application_Log_Setting__mdt logSetting) {
145+
if (isSandbox) {
146+
return logSetting.Slack_Hook_Sandbox__c;
147+
}
148+
149+
return String.isNotBlank(logSetting.Named_Credential__c)
150+
? 'callout:' + logSetting.Named_Credential__c
151+
: logSetting.Slack_Hook__c;
152+
}
153+
154+
/**
155+
* @description: Returns the log level ordinal for a input loglevel string
156+
INFO = 0, WARNING = 1, ERROR = 2, CRITICAL = 3
157+
* @author Stian Ruud Schikora | 02-22-2021
158+
* @param logLevel
159+
* @return Integer
160+
**/
161+
private static Integer getLogLevelOrdinal(String logLevel) {
162+
return LoggerUtility.inverseLevelMap.get(logLevel).ordinal();
163+
}
164+
165+
private class AppLog {
166+
private AppLog(
167+
Application_Log__c appLog,
168+
String slackHook,
169+
String messageTemplate
170+
) {
171+
this.logRecord = appLog;
172+
this.slackHook = slackHook;
173+
this.messageTemplate = messageTemplate;
174+
}
175+
176+
private Application_Log__c logRecord;
177+
private String slackHook;
178+
private String messageTemplate;
179+
}
180+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>50.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>

0 commit comments

Comments
 (0)