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

Features for 1.0.0 of package #60

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 18 additions & 2 deletions cds-plugin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const cds = require("@sap/cds/lib");
const path = require('path');
const { preprocessTypes } = require("./lib/buildNotificationTypes");
const { existsSync } = require("fs");

if (cds.cli.command === "build") {
// register build plugin
Expand All @@ -10,11 +13,24 @@ if (cds.cli.command === "build") {

else cds.once("served", async () => {
const { validateNotificationTypes, readFile } = require("./lib/utils");
const { createNotificationTypesMap } = require("./lib/notificationTypes");
const { createNotificationTypesMap } = require("./lib/deployer/notificationTypes");
const production = cds.env.profiles?.includes("production");

// read notification types
const notificationTypes = readFile(cds.env.requires?.notifications?.types);
const notificationTypes = require(path.join(cds.root, cds.env.requires?.notifications?.types));

if (existsSync(cds.env.requires.notifications?.build?.before)) {
const handler = require(cds.env.requires.notifications?.build?.before);
await handler(notificationTypes);
}

preprocessTypes(notificationTypes);

if (existsSync(cds.env.requires.notifications?.build?.after)) {
const handler = require(path.join(cds.root, cds.env.requires.notifications?.build?.after));
await handler(notificationTypes);
}

if (validateNotificationTypes(notificationTypes)) {
if (!production) {
const notificationTypesMap = createNotificationTypesMap(notificationTypes, true);
Expand Down
1 change: 1 addition & 0 deletions index.cds
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
using from './lib/notifications';
36 changes: 32 additions & 4 deletions lib/build.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
const cds = require('@sap/cds')
const cds = require('@sap/cds');
const { readFile, getPrefix } = require('./utils');
const { preprocessTypes } = require('./buildNotificationTypes');
const { readFileSync } = require('fs');
const { BuildPlugin } = cds.build

const { copy, exists, path } = cds.utils
const { copy, exists, path, write } = cds.utils

module.exports = class NotificationsBuildPlugin extends BuildPlugin {

static taskDefaults = { src: cds.env.folders.srv }
static hasTask() {
const notificationTypesFile = cds.env.requires?.notifications?.types;
return notificationTypesFile === undefined ? false : exists(notificationTypesFile);
}

async build() {
if (exists(cds.env.requires.notifications?.types)) {
const notificationTypes = require(path.join(cds.root, cds.env.requires.notifications.types));

if (exists(cds.env.requires.notifications?.build?.before)) {
const handler = require(cds.env.requires.notifications?.build?.before);
await handler(notificationTypes);
}

preprocessTypes(notificationTypes);

if (exists(cds.env.requires.notifications?.build?.after)) {
const handler = require(path.join(cds.root, cds.env.requires.notifications?.build?.after));
await handler(notificationTypes);
}

const fileName = path.basename(cds.env.requires.notifications.types);
await copy(cds.env.requires.notifications.types).to(path.join(this.task.dest, fileName));
await this.write(JSON.stringify(notificationTypes)).to(path.join(this.task.dest, fileName));

await this.write(JSON.stringify(notificationTypes)).to(path.join(this.task.dest, '../notifications/notification-types.json'));

if (exists(path.join(this.task.src, '../node_modules/@cap-js/notifications/lib/deployer'))) {
await this.copy(path.join(this.task.src, '../node_modules/@cap-js/notifications/lib/deployer')).to(path.join(this.task.dest, '../notifications'))
}
const config = {
prefix: getPrefix(),
destination: cds.env.requires.notifications?.destination ?? "SAP_Notifications"
}
await this.write(JSON.stringify(config)).to(path.join(this.task.dest, '../notifications/config.json'))
}
}
}
103 changes: 103 additions & 0 deletions lib/buildNotificationTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const { supportedANSLanguages } = require("./utils");
const cds = require("@sap/cds");
const path = require("path");
const {TextBundle} = require('@sap/textbundle');

function buildNotificationType(_) {
const notificationType = {
NotificationTypeKey: _.NotificationTypeKey,
NotificationTypeVersion: _.NotificationTypeVersion,
Templates: _.Templates?.map(t => ({
Language: t.Language,
TemplateSensitive: t.TemplateSensitive,
TemplatePublic: t.TemplatePublic,
TemplateGrouped: t.TemplateGrouped,
Subtitle: t.Subtitle,
Description: t.Description,
TemplateLanguage: t.TemplateLanguage,
EmailSubject: t.EmailSubject,
EmailHtml: t.EmailHtml,
EmailText: t.EmailText
})),
Actions: _.Actions?.map(a => ({
ActionId: a.ActionId,
ActionText: a.ActionText,
GroupActionText: a.GroupActionText,
})),
DeliveryChannels: _.DeliveryChannels?.map(d => ({
Type: d.Type,
Enabled: d.Enabled,
DefaultPreference: d.DefaultPreference,
EditablePreference: d.EditablePreference
}))
}
return JSON.parse(JSON.stringify(notificationType));
}

const preprocessTypes = function (types) {
if (!cds.env.requires.notifications) cds.env.requires.notifications = {}
if (!cds.env.requires.notifications.defaults) cds.env.requires.notifications.defaults = {}

for (let i = 0; i < types.length; i++) {
const notificationType = types[i]
if (notificationType.ID) {
notificationType.NotificationTypeKey = notificationType.ID;
}
if (!notificationType.NotificationTypeVersion) notificationType.NotificationTypeVersion = "1"
if (!notificationType.Templates) { //-> Languages not manually specified
notificationType.Templates = [];
for (const language in supportedANSLanguages) {
const textBundle = new TextBundle(path.join(cds.root, (cds.env.i18n?.folders[0] ?? '_i18n') + '/notifications' ), language);
const newTemplate = {
Language: language,
TemplateLanguage: 'Mustache'
}
const getVal = (property) => textBundle.getText(notificationType[property]?.code ?? notificationType[property], (notificationType[property]?.args ?? []).map(a => `{{${a}}}`))
if (notificationType.TemplateSensitive) newTemplate.TemplateSensitive = getVal('TemplateSensitive');
if (notificationType.TemplatePublic) newTemplate.TemplatePublic = getVal('TemplatePublic');
if (notificationType.TemplateGrouped) newTemplate.TemplateGrouped = getVal('TemplateGrouped');
if (notificationType.Subtitle) newTemplate.Subtitle = getVal('Subtitle');
if (notificationType.Description) newTemplate.Description = getVal('Description');
if (notificationType.EmailSubject) newTemplate.EmailSubject = getVal('EmailSubject');
if (notificationType.EmailHtml) newTemplate.EmailHtml = getVal('EmailHtml');
if (notificationType.EmailText) newTemplate.EmailText = getVal('EmailText');
notificationType.Templates.push(newTemplate)
}
}

if (notificationType.DeliveryChannels && !Array.isArray(notificationType.DeliveryChannels)) {
const deliveryChannels = []
for (const option in notificationType.DeliveryChannels) {
deliveryChannels.push({
Type: option.toUpperCase(),
Enabled: !!notificationType.DeliveryChannels[option],
DefaultPreference: !!notificationType.DeliveryChannels[option],
EditablePreference: !!notificationType.DeliveryChannels[option]
})
}
notificationType.DeliveryChannels = deliveryChannels;
}

//
if (!cds.env.requires.notifications.defaults[notificationType.NotificationTypeKey])
cds.env.requires.notifications.defaults[notificationType.NotificationTypeKey] = {}
if (!cds.env.requires.notifications.defaults[notificationType.NotificationTypeKey][notificationType.NotificationTypeVersion]) {
cds.env.requires.notifications.defaults[notificationType.NotificationTypeKey][notificationType.NotificationTypeVersion] = {
priority: notificationType.Priority ?? 'Neutral',
navigation: notificationType.Navigation ? {
semanticObject : notificationType.SemanticObject ?? notificationType.semanticObject,
semanticAction : notificationType.SemanticAction ?? notificationType.semanticAction,
} : null,
minTimeBetweenNotifications: notificationType.MinTimeBetweenNotifications ?? 0,
properties : notificationType.Properties ?? {},
targetParameters : notificationType.TargetParameters ? new Set(...notificationType.TargetParameters) : new Set()
}
}

types[i] = buildNotificationType(notificationType)
}
}

module.exports = {
preprocessTypes
}
22 changes: 0 additions & 22 deletions lib/content-deployment.js

This file was deleted.

17 changes: 17 additions & 0 deletions lib/deployer/content-deployment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { readFileSync } = require('fs');
const { processNotificationTypes } = require("./notificationTypes");
const { setGlobalLogLevel } = require("@sap-cloud-sdk/util");

async function deployNotificationTypes() {
setGlobalLogLevel("error");

// read notification types
const notificationTypes = JSON.parse(readFileSync('./notification-types.json'));
await processNotificationTypes(notificationTypes);
}

deployNotificationTypes();

module.exports = {
deployNotificationTypes
}
17 changes: 10 additions & 7 deletions lib/notificationTypes.js → lib/deployer/notificationTypes.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const { executeHttpRequest } = require("@sap-cloud-sdk/http-client");
const { buildHeadersForDestination } = require("@sap-cloud-sdk/connectivity");
const { createLogger } = require("@sap-cloud-sdk/util");
const LOG = createLogger('notifications');
Comment on lines +3 to +4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the advantage of using @sap-cloud-sdk/util over cds.log. IMO cds.log should cover all the required features and if not those should be requested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was that the deployer module runs with the bare minimum required dependencies and utils is anyways needed for executeHttpRequest thus @sao/cds as a dependency for the deployer can be avoided.

const { getNotificationDestination, getPrefix, getNotificationTypesKeyWithPrefix } = require("./utils");
const NOTIFICATION_TYPES_API_ENDPOINT = "v2/NotificationType.svc";
const cds = require("@sap/cds");
const LOG = cds.log('notifications');

const defaultTemplate = {
NotificationTypeKey: "Default",
Expand Down Expand Up @@ -52,6 +52,9 @@ function createNotificationTypesMap(notificationTypesJSON, isLocal = false) {
types[notificationTypeKeyWithPrefix] = {};
}

//Process smart types
//-> Simpler channels + Easier localisation of templates

types[notificationTypeKeyWithPrefix][notificationType.NotificationTypeVersion] = notificationType;
});

Expand All @@ -73,7 +76,7 @@ async function createNotificationType(notificationType) {
url: NOTIFICATION_TYPES_API_ENDPOINT
});

LOG._warn && LOG.warn(
LOG.warn(
`Notification Type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion} was not found. Creating it...`
);

Expand All @@ -92,7 +95,7 @@ async function updateNotificationType(id, notificationType) {
url: NOTIFICATION_TYPES_API_ENDPOINT
});

LOG._info && LOG.info(
LOG.info(
`Detected change in notification type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion}. Updating it...`
);

Expand All @@ -111,7 +114,7 @@ async function deleteNotificationType(notificationType) {
url: NOTIFICATION_TYPES_API_ENDPOINT
});

LOG._info && LOG.info(
LOG.info(
`Notification Type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion} not present in the types file. Deleting it...`
);

Expand Down Expand Up @@ -257,7 +260,7 @@ async function processNotificationTypes(notificationTypesJSON) {
}

if (!existingType.NotificationTypeKey.startsWith(`${prefix}/`)) {
LOG._info && LOG.info(`Skipping Notification Type of other application: ${existingType.NotificationTypeKey}.`);
LOG.info(`Skipping Notification Type of other application: ${existingType.NotificationTypeKey}.`);
continue;
}

Expand All @@ -276,7 +279,7 @@ async function processNotificationTypes(notificationTypesJSON) {
if (!isNotificationTypeEqual(existingType, newType)) {
await updateNotificationType(existingType.NotificationTypeId, notificationTypes[existingType.NotificationTypeKey][existingType.NotificationTypeVersion]);
} else {
LOG._info && LOG.info(
LOG.info(
`Notification Type of key ${existingType.NotificationTypeKey} and version ${existingType.NotificationTypeVersion} unchanged.`
);
}
Expand Down
12 changes: 12 additions & 0 deletions lib/deployer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "@cap-js/notifications-deployer",
"dependencies": {
"@sap-cloud-sdk/connectivity": "^3.13.0",
"@sap-cloud-sdk/http-client": "^3.13.0",
"@sap-cloud-sdk/util": "^3.13.0"
},
"scripts": {
"start": "node ./content-deployment.js"
}
}

55 changes: 55 additions & 0 deletions lib/deployer/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { getDestination } = require("@sap-cloud-sdk/connectivity");
const {existsSync} = require('fs')
const messages = {
DESTINATION_NOT_FOUND: "Failed to get destination: ",
};

let prefix // be filled in below...
function getPrefixCdsEnv() {
if (!prefix) {
prefix = cds.env.requires.notifications?.prefix
if (prefix === "$app-name") try {
prefix = require(cds.root + '/package.json').name
} catch { prefix = null }
if (!prefix) prefix = basename(cds.root)
}
return prefix
}

function getConfig() {
if (existsSync('./config.json')) {
return JSON.parse(require('./config.json'));
} else {
return {
destination: cds.env.requires.notifications?.destination ?? "SAP_Notifications",
prefix: getPrefixCdsEnv()
}
}
}

async function getNotificationDestination() {
const config = getConfig();
const destinationName = config.destination ?? "SAP_Notifications";
const notificationDestination = await getDestination({ destinationName, useCache: true });
if (!notificationDestination) {
// TODO: What to do if destination isn't found??
throw new Error(messages.DESTINATION_NOT_FOUND + destinationName);
}
return notificationDestination;
}

function getPrefix() {
const config = getConfig();
return config.prefix;
}

function getNotificationTypesKeyWithPrefix(notificationTypeKey) {
const prefix = getPrefix();
return `${prefix}/${notificationTypeKey}`;
}

module.exports = {
getNotificationDestination,
getPrefix,
getNotificationTypesKeyWithPrefix,
};
Loading
Loading