@medusajs/notification 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/joiner-config.d.ts +5 -0
- package/dist/joiner-config.js +30 -0
- package/dist/loaders/providers.d.ts +5 -0
- package/dist/loaders/providers.js +74 -0
- package/dist/migrations/Migration20240509083918_InitialSetupMigration.d.ts +4 -0
- package/dist/migrations/Migration20240509083918_InitialSetupMigration.js +17 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +10 -0
- package/dist/models/notification-provider.d.ts +12 -0
- package/dist/models/notification-provider.js +72 -0
- package/dist/models/notification.d.ts +19 -0
- package/dist/models/notification.js +122 -0
- package/dist/module-definition.d.ts +2 -0
- package/dist/module-definition.js +14 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +10 -0
- package/dist/services/notification-module-service.d.ts +26 -0
- package/dist/services/notification-module-service.js +79 -0
- package/dist/services/notification-provider.d.ts +17 -0
- package/dist/services/notification-provider.js +33 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/index.d.ts +0 -0
- package/dist/utils/index.js +0 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Notification Module
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const module_definition_1 = require("./module-definition");
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./models"), exports);
|
|
20
|
+
__exportStar(require("./services"), exports);
|
|
21
|
+
exports.default = module_definition_1.moduleDefinition;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ModuleJoinerConfig } from "@medusajs/types";
|
|
2
|
+
import { MapToConfig } from "@medusajs/utils";
|
|
3
|
+
export declare const LinkableKeys: Record<string, string>;
|
|
4
|
+
export declare const entityNameToLinkableKeysMap: MapToConfig;
|
|
5
|
+
export declare const joinerConfig: ModuleJoinerConfig;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.joinerConfig = exports.entityNameToLinkableKeysMap = exports.LinkableKeys = void 0;
|
|
4
|
+
const modules_sdk_1 = require("@medusajs/modules-sdk");
|
|
5
|
+
const _models_1 = require("./models");
|
|
6
|
+
exports.LinkableKeys = {
|
|
7
|
+
notification_id: _models_1.NotificationModel.name,
|
|
8
|
+
};
|
|
9
|
+
const entityLinkableKeysMap = {};
|
|
10
|
+
Object.entries(exports.LinkableKeys).forEach(([key, value]) => {
|
|
11
|
+
entityLinkableKeysMap[value] ??= [];
|
|
12
|
+
entityLinkableKeysMap[value].push({
|
|
13
|
+
mapTo: key,
|
|
14
|
+
valueFrom: key.split("_").pop(),
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
exports.entityNameToLinkableKeysMap = entityLinkableKeysMap;
|
|
18
|
+
exports.joinerConfig = {
|
|
19
|
+
serviceName: modules_sdk_1.Modules.NOTIFICATION,
|
|
20
|
+
primaryKeys: ["id"],
|
|
21
|
+
linkableKeys: exports.LinkableKeys,
|
|
22
|
+
alias: [
|
|
23
|
+
{
|
|
24
|
+
name: ["notification", "notifications"],
|
|
25
|
+
args: {
|
|
26
|
+
entity: _models_1.NotificationModel.name,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { LoaderOptions, ModuleProvider, ModulesSdkTypes } from "@medusajs/types";
|
|
2
|
+
declare const _default: ({ container, options, }: LoaderOptions<(ModulesSdkTypes.ModuleServiceInitializeOptions | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions) & {
|
|
3
|
+
providers: ModuleProvider[];
|
|
4
|
+
}>) => Promise<void>;
|
|
5
|
+
export default _default;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const modules_sdk_1 = require("@medusajs/modules-sdk");
|
|
4
|
+
const utils_1 = require("@medusajs/utils");
|
|
5
|
+
const _services_1 = require("../services");
|
|
6
|
+
const _types_1 = require("../types");
|
|
7
|
+
const awilix_1 = require("awilix");
|
|
8
|
+
const registrationFn = async (klass, container, pluginOptions) => {
|
|
9
|
+
Object.entries(pluginOptions.config || []).map(([name, config]) => {
|
|
10
|
+
container.register({
|
|
11
|
+
[_types_1.NotificationProviderRegistrationPrefix + name]: (0, awilix_1.asFunction)((cradle) => new klass(cradle, config), {
|
|
12
|
+
lifetime: klass.LIFE_TIME || awilix_1.Lifetime.SINGLETON,
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
container.registerAdd(_types_1.NotificationIdentifiersRegistrationName, (0, awilix_1.asValue)(name));
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
exports.default = async ({ container, options, }) => {
|
|
19
|
+
await (0, modules_sdk_1.moduleProviderLoader)({
|
|
20
|
+
container,
|
|
21
|
+
providers: options?.providers || [],
|
|
22
|
+
registerServiceFn: registrationFn,
|
|
23
|
+
});
|
|
24
|
+
await syncDatabaseProviders({
|
|
25
|
+
container,
|
|
26
|
+
providers: options?.providers || [],
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
async function syncDatabaseProviders({ container, providers, }) {
|
|
30
|
+
const providerServiceRegistrationKey = (0, utils_1.lowerCaseFirst)(_services_1.NotificationProviderService.name);
|
|
31
|
+
const providerService = container.resolve(providerServiceRegistrationKey);
|
|
32
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER) ?? console;
|
|
33
|
+
const normalizedProviders = providers.map((provider) => {
|
|
34
|
+
const [name, config] = Object.entries(provider.options?.config)?.[0];
|
|
35
|
+
if (!name) {
|
|
36
|
+
throw new Error("An entry in the provider config is required to initialize notification providers");
|
|
37
|
+
}
|
|
38
|
+
const id = name;
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
handle: name,
|
|
42
|
+
name: config?.name ?? name,
|
|
43
|
+
is_enabled: true,
|
|
44
|
+
channels: config?.channels ?? [],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
validateProviders(normalizedProviders);
|
|
48
|
+
try {
|
|
49
|
+
const providersInDb = await providerService.list({});
|
|
50
|
+
const providersToDisable = providersInDb.filter((dbProvider) => !normalizedProviders.some((normalizedProvider) => normalizedProvider.id === dbProvider.id));
|
|
51
|
+
const promises = [];
|
|
52
|
+
if (normalizedProviders.length) {
|
|
53
|
+
promises.push(providerService.upsert(normalizedProviders));
|
|
54
|
+
}
|
|
55
|
+
if (providersToDisable.length) {
|
|
56
|
+
promises.push(providerService.update(providersToDisable.map((p) => ({ id: p.id, is_enabled: false }))));
|
|
57
|
+
}
|
|
58
|
+
await (0, utils_1.promiseAll)(promises);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
logger.error(`Error syncing the notification providers: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function validateProviders(providers) {
|
|
65
|
+
const hasForChannel = {};
|
|
66
|
+
providers.forEach((provider) => {
|
|
67
|
+
provider.channels.forEach((channel) => {
|
|
68
|
+
if (hasForChannel[channel]) {
|
|
69
|
+
throw new Error(`Multiple providers are configured for the same channel: ${channel}`);
|
|
70
|
+
}
|
|
71
|
+
hasForChannel[channel] = true;
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Migration20240509083918_InitialSetupMigration = void 0;
|
|
4
|
+
const migrations_1 = require("@mikro-orm/migrations");
|
|
5
|
+
class Migration20240509083918_InitialSetupMigration extends migrations_1.Migration {
|
|
6
|
+
async up() {
|
|
7
|
+
this.addSql("drop table if exists notification");
|
|
8
|
+
this.addSql("drop table if exists notification_provider");
|
|
9
|
+
this.addSql('create table if not exists "notification_provider" ("id" text not null, "handle" text not null, "name" text not null, "is_enabled" boolean not null default true, "channels" text[] not null, constraint "notification_provider_pkey" primary key ("id"));');
|
|
10
|
+
this.addSql('create table if not exists "notification" ("id" text not null, "to" text not null, "channel" text not null, "template" text not null, "data" jsonb null, "trigger_type" text null, "resource_id" text null, "resource_type" text null, "receiver_id" text null, "original_notification_id" text null, "idempotency_key" text null, "external_id" text null, "provider_id" text null, "created_at" timestamptz not null default now(), constraint "notification_pkey" primary key ("id"));');
|
|
11
|
+
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_notification_provider_id" ON "notification" (provider_id);');
|
|
12
|
+
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_notification_idempotency_key" ON "notification" (idempotency_key);');
|
|
13
|
+
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_notification_receiver_id" ON "notification" (receiver_id);');
|
|
14
|
+
this.addSql('alter table if exists "notification" add constraint "notification_provider_id_foreign" foreign key ("provider_id") references "notification_provider" ("id") on update cascade on delete set null;');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.Migration20240509083918_InitialSetupMigration = Migration20240509083918_InitialSetupMigration;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NotificationProvider = exports.NotificationModel = void 0;
|
|
7
|
+
var notification_1 = require("./notification");
|
|
8
|
+
Object.defineProperty(exports, "NotificationModel", { enumerable: true, get: function () { return __importDefault(notification_1).default; } });
|
|
9
|
+
var notification_provider_1 = require("./notification-provider");
|
|
10
|
+
Object.defineProperty(exports, "NotificationProvider", { enumerable: true, get: function () { return __importDefault(notification_provider_1).default; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Collection } from "@mikro-orm/core";
|
|
2
|
+
import NotificationModel from "./notification";
|
|
3
|
+
export default class NotificationProvider {
|
|
4
|
+
id: string;
|
|
5
|
+
handle: string;
|
|
6
|
+
name: string;
|
|
7
|
+
is_enabled: boolean;
|
|
8
|
+
channels: string[];
|
|
9
|
+
notifications: Collection<NotificationModel, object>;
|
|
10
|
+
onCreate(): void;
|
|
11
|
+
onInit(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const utils_1 = require("@medusajs/utils");
|
|
16
|
+
const core_1 = require("@mikro-orm/core");
|
|
17
|
+
const notification_1 = __importDefault(require("./notification"));
|
|
18
|
+
let NotificationProvider = class NotificationProvider {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.is_enabled = true;
|
|
21
|
+
this.notifications = new core_1.Collection(this);
|
|
22
|
+
}
|
|
23
|
+
onCreate() {
|
|
24
|
+
this.id = (0, utils_1.generateEntityId)(this.id, "notpro");
|
|
25
|
+
}
|
|
26
|
+
onInit() {
|
|
27
|
+
this.id = (0, utils_1.generateEntityId)(this.id, "notpro");
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, core_1.PrimaryKey)({ columnType: "text" }),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], NotificationProvider.prototype, "id", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, core_1.Property)({ columnType: "text" }),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], NotificationProvider.prototype, "handle", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, core_1.Property)({ columnType: "text" }),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], NotificationProvider.prototype, "name", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, core_1.Property)({ columnType: "boolean", defaultRaw: "true" }),
|
|
44
|
+
__metadata("design:type", Boolean)
|
|
45
|
+
], NotificationProvider.prototype, "is_enabled", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
(0, core_1.Property)({ type: core_1.ArrayType }),
|
|
48
|
+
__metadata("design:type", Array)
|
|
49
|
+
], NotificationProvider.prototype, "channels", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
(0, core_1.OneToMany)({
|
|
52
|
+
entity: () => notification_1.default,
|
|
53
|
+
mappedBy: (notification) => notification.provider_id,
|
|
54
|
+
}),
|
|
55
|
+
__metadata("design:type", Object)
|
|
56
|
+
], NotificationProvider.prototype, "notifications", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, core_1.BeforeCreate)(),
|
|
59
|
+
__metadata("design:type", Function),
|
|
60
|
+
__metadata("design:paramtypes", []),
|
|
61
|
+
__metadata("design:returntype", void 0)
|
|
62
|
+
], NotificationProvider.prototype, "onCreate", null);
|
|
63
|
+
__decorate([
|
|
64
|
+
(0, core_1.OnInit)(),
|
|
65
|
+
__metadata("design:type", Function),
|
|
66
|
+
__metadata("design:paramtypes", []),
|
|
67
|
+
__metadata("design:returntype", void 0)
|
|
68
|
+
], NotificationProvider.prototype, "onInit", null);
|
|
69
|
+
NotificationProvider = __decorate([
|
|
70
|
+
(0, core_1.Entity)()
|
|
71
|
+
], NotificationProvider);
|
|
72
|
+
exports.default = NotificationProvider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import NotificationProvider from "./notification-provider";
|
|
2
|
+
export default class NotificationModel {
|
|
3
|
+
id: string;
|
|
4
|
+
to: string;
|
|
5
|
+
channel: string;
|
|
6
|
+
template: string;
|
|
7
|
+
data: Record<string, unknown> | null;
|
|
8
|
+
trigger_type?: string | null;
|
|
9
|
+
resource_id?: string | null;
|
|
10
|
+
resource_type?: string | null;
|
|
11
|
+
receiver_id?: string | null;
|
|
12
|
+
original_notification_id?: string | null;
|
|
13
|
+
idempotency_key?: string | null;
|
|
14
|
+
external_id?: string | null;
|
|
15
|
+
provider_id: string;
|
|
16
|
+
provider: NotificationProvider;
|
|
17
|
+
created_at: Date;
|
|
18
|
+
onCreate(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const utils_1 = require("@medusajs/utils");
|
|
16
|
+
const core_1 = require("@mikro-orm/core");
|
|
17
|
+
const notification_provider_1 = __importDefault(require("./notification-provider"));
|
|
18
|
+
const NotificationProviderIdIndex = (0, utils_1.createPsqlIndexStatementHelper)({
|
|
19
|
+
tableName: "notification",
|
|
20
|
+
columns: "provider_id",
|
|
21
|
+
});
|
|
22
|
+
const NotificationIdempotencyKeyIndex = (0, utils_1.createPsqlIndexStatementHelper)({
|
|
23
|
+
tableName: "notification",
|
|
24
|
+
columns: "idempotency_key",
|
|
25
|
+
});
|
|
26
|
+
const NotificationReceiverIdIndex = (0, utils_1.createPsqlIndexStatementHelper)({
|
|
27
|
+
tableName: "notification",
|
|
28
|
+
columns: "receiver_id",
|
|
29
|
+
});
|
|
30
|
+
// We don't need to support soft deletes here as this information is mainly used for auditing purposes.
|
|
31
|
+
// Instead, we probably want to have a TTL for each entry, so we don't bloat the DB (and also for GDPR reasons if TTL < 30 days).
|
|
32
|
+
let NotificationModel = class NotificationModel {
|
|
33
|
+
onCreate() {
|
|
34
|
+
this.id = (0, utils_1.generateEntityId)(this.id, "noti");
|
|
35
|
+
this.provider_id ??= this.provider_id ?? this.provider?.id;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, core_1.PrimaryKey)({ columnType: "text" }),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], NotificationModel.prototype, "id", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, core_1.Property)({ columnType: "text" }),
|
|
44
|
+
__metadata("design:type", String)
|
|
45
|
+
], NotificationModel.prototype, "to", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
(0, core_1.Property)({ columnType: "text" }),
|
|
48
|
+
__metadata("design:type", String)
|
|
49
|
+
], NotificationModel.prototype, "channel", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
(0, core_1.Property)({ columnType: "text" }),
|
|
52
|
+
__metadata("design:type", String)
|
|
53
|
+
], NotificationModel.prototype, "template", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
(0, core_1.Property)({ columnType: "jsonb", nullable: true }),
|
|
56
|
+
__metadata("design:type", Object)
|
|
57
|
+
], NotificationModel.prototype, "data", void 0);
|
|
58
|
+
__decorate([
|
|
59
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
60
|
+
__metadata("design:type", Object)
|
|
61
|
+
], NotificationModel.prototype, "trigger_type", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
64
|
+
__metadata("design:type", Object)
|
|
65
|
+
], NotificationModel.prototype, "resource_id", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
68
|
+
__metadata("design:type", Object)
|
|
69
|
+
], NotificationModel.prototype, "resource_type", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
72
|
+
__metadata("design:type", Object)
|
|
73
|
+
], NotificationModel.prototype, "receiver_id", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
76
|
+
__metadata("design:type", Object)
|
|
77
|
+
], NotificationModel.prototype, "original_notification_id", void 0);
|
|
78
|
+
__decorate([
|
|
79
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
80
|
+
__metadata("design:type", Object)
|
|
81
|
+
], NotificationModel.prototype, "idempotency_key", void 0);
|
|
82
|
+
__decorate([
|
|
83
|
+
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
|
84
|
+
__metadata("design:type", Object)
|
|
85
|
+
], NotificationModel.prototype, "external_id", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
(0, core_1.ManyToOne)(() => notification_provider_1.default, {
|
|
88
|
+
columnType: "text",
|
|
89
|
+
fieldName: "provider_id",
|
|
90
|
+
mapToPk: true,
|
|
91
|
+
nullable: true,
|
|
92
|
+
onDelete: "set null",
|
|
93
|
+
}),
|
|
94
|
+
__metadata("design:type", String)
|
|
95
|
+
], NotificationModel.prototype, "provider_id", void 0);
|
|
96
|
+
__decorate([
|
|
97
|
+
(0, core_1.ManyToOne)(() => notification_provider_1.default, { persist: false }),
|
|
98
|
+
__metadata("design:type", notification_provider_1.default)
|
|
99
|
+
], NotificationModel.prototype, "provider", void 0);
|
|
100
|
+
__decorate([
|
|
101
|
+
(0, core_1.Property)({
|
|
102
|
+
onCreate: () => new Date(),
|
|
103
|
+
columnType: "timestamptz",
|
|
104
|
+
defaultRaw: "now()",
|
|
105
|
+
}),
|
|
106
|
+
__metadata("design:type", Date)
|
|
107
|
+
], NotificationModel.prototype, "created_at", void 0);
|
|
108
|
+
__decorate([
|
|
109
|
+
(0, core_1.BeforeCreate)(),
|
|
110
|
+
(0, core_1.OnInit)(),
|
|
111
|
+
__metadata("design:type", Function),
|
|
112
|
+
__metadata("design:paramtypes", []),
|
|
113
|
+
__metadata("design:returntype", void 0)
|
|
114
|
+
], NotificationModel.prototype, "onCreate", null);
|
|
115
|
+
NotificationModel = __decorate([
|
|
116
|
+
NotificationProviderIdIndex.MikroORMIndex(),
|
|
117
|
+
NotificationIdempotencyKeyIndex.MikroORMIndex(),
|
|
118
|
+
NotificationReceiverIdIndex.MikroORMIndex(),
|
|
119
|
+
(0, core_1.Entity)({ tableName: "notification" })
|
|
120
|
+
// Since there is a native `Notification` type, we have to call this something else here and in a couple of other places.
|
|
121
|
+
], NotificationModel);
|
|
122
|
+
exports.default = NotificationModel;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.moduleDefinition = void 0;
|
|
7
|
+
const _services_1 = require("./services");
|
|
8
|
+
const providers_1 = __importDefault(require("./loaders/providers"));
|
|
9
|
+
const service = _services_1.NotificationModuleService;
|
|
10
|
+
const loaders = [providers_1.default];
|
|
11
|
+
exports.moduleDefinition = {
|
|
12
|
+
service,
|
|
13
|
+
loaders,
|
|
14
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NotificationProviderService = exports.NotificationModuleService = void 0;
|
|
7
|
+
var notification_module_service_1 = require("./notification-module-service");
|
|
8
|
+
Object.defineProperty(exports, "NotificationModuleService", { enumerable: true, get: function () { return __importDefault(notification_module_service_1).default; } });
|
|
9
|
+
var notification_provider_1 = require("./notification-provider");
|
|
10
|
+
Object.defineProperty(exports, "NotificationProviderService", { enumerable: true, get: function () { return __importDefault(notification_provider_1).default; } });
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Context, DAL, NotificationTypes, INotificationModuleService, InternalModuleDeclaration, ModuleJoinerConfig, ModulesSdkTypes } from "@medusajs/types";
|
|
2
|
+
import { ModulesSdkUtils } from "@medusajs/utils";
|
|
3
|
+
import NotificationProviderService from "./notification-provider";
|
|
4
|
+
import { NotificationModel } from "../models";
|
|
5
|
+
type InjectedDependencies = {
|
|
6
|
+
baseRepository: DAL.RepositoryService;
|
|
7
|
+
notificationModelService: ModulesSdkTypes.InternalModuleService<any>;
|
|
8
|
+
notificationProviderService: NotificationProviderService;
|
|
9
|
+
};
|
|
10
|
+
declare const NotificationModuleService_base: new (container: InjectedDependencies) => ModulesSdkUtils.AbstractModuleService<InjectedDependencies, NotificationTypes.NotificationDTO, {
|
|
11
|
+
NotificationProvider: {
|
|
12
|
+
dto: NotificationTypes.NotificationProviderDTO;
|
|
13
|
+
};
|
|
14
|
+
}>;
|
|
15
|
+
export default class NotificationModuleService<TEntity extends NotificationModel = NotificationModel> extends NotificationModuleService_base implements INotificationModuleService {
|
|
16
|
+
protected readonly moduleDeclaration: InternalModuleDeclaration;
|
|
17
|
+
protected baseRepository_: DAL.RepositoryService;
|
|
18
|
+
protected readonly notificationService_: ModulesSdkTypes.InternalModuleService<TEntity>;
|
|
19
|
+
protected readonly notificationProviderService_: NotificationProviderService;
|
|
20
|
+
constructor({ baseRepository, notificationModelService, notificationProviderService, }: InjectedDependencies, moduleDeclaration: InternalModuleDeclaration);
|
|
21
|
+
__joinerConfig(): ModuleJoinerConfig;
|
|
22
|
+
create(data: NotificationTypes.CreateNotificationDTO[], sharedContext?: Context): Promise<NotificationTypes.NotificationDTO[]>;
|
|
23
|
+
create(data: NotificationTypes.CreateNotificationDTO, sharedContext?: Context): Promise<NotificationTypes.NotificationDTO>;
|
|
24
|
+
protected create_(data: NotificationTypes.CreateNotificationDTO[], sharedContext?: Context): Promise<TEntity[]>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const utils_1 = require("@medusajs/utils");
|
|
16
|
+
const joiner_config_1 = require("../joiner-config");
|
|
17
|
+
const _models_1 = require("../models");
|
|
18
|
+
const generateMethodForModels = [_models_1.NotificationProvider];
|
|
19
|
+
class NotificationModuleService extends utils_1.ModulesSdkUtils.abstractModuleServiceFactory(_models_1.NotificationModel, generateMethodForModels, joiner_config_1.entityNameToLinkableKeysMap) {
|
|
20
|
+
constructor({ baseRepository, notificationModelService, notificationProviderService, }, moduleDeclaration) {
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.moduleDeclaration = moduleDeclaration;
|
|
24
|
+
this.baseRepository_ = baseRepository;
|
|
25
|
+
this.notificationService_ = notificationModelService;
|
|
26
|
+
this.notificationProviderService_ = notificationProviderService;
|
|
27
|
+
}
|
|
28
|
+
__joinerConfig() {
|
|
29
|
+
return joiner_config_1.joinerConfig;
|
|
30
|
+
}
|
|
31
|
+
async create(data, sharedContext = {}) {
|
|
32
|
+
const normalized = Array.isArray(data) ? data : [data];
|
|
33
|
+
const createdNotifications = await this.create_(normalized, sharedContext);
|
|
34
|
+
const serialized = await this.baseRepository_.serialize(createdNotifications);
|
|
35
|
+
return Array.isArray(data) ? serialized : serialized[0];
|
|
36
|
+
}
|
|
37
|
+
async create_(data, sharedContext = {}) {
|
|
38
|
+
if (!data.length) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
// TODO: At this point we should probably take a lock with the idempotency keys so we don't have race conditions.
|
|
42
|
+
// Also, we should probably rely on Redis for this instead of the database.
|
|
43
|
+
const idempotencyKeys = data
|
|
44
|
+
.map((entry) => entry.idempotency_key)
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
const alreadySentNotifications = await this.notificationService_.list({
|
|
47
|
+
idempotency_key: idempotencyKeys,
|
|
48
|
+
}, { take: null }, sharedContext);
|
|
49
|
+
const existsMap = new Map(alreadySentNotifications.map((n) => [n.idempotency_key, true]));
|
|
50
|
+
const notificationsToProcess = data.filter((entry) => !existsMap.has(entry.idempotency_key));
|
|
51
|
+
const notificationsToCreate = await (0, utils_1.promiseAll)(notificationsToProcess.map(async (entry) => {
|
|
52
|
+
const provider = await this.notificationProviderService_.getProviderForChannel(entry.channel);
|
|
53
|
+
if (!provider) {
|
|
54
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, `Could not find a notification provider for channel: ${entry.channel}`);
|
|
55
|
+
}
|
|
56
|
+
const res = await this.notificationProviderService_.send(provider, entry);
|
|
57
|
+
return { ...entry, provider_id: provider.id, external_id: res.id };
|
|
58
|
+
}));
|
|
59
|
+
// Currently we store notifications after they are sent, which might result in a notification being sent that is not registered in the database.
|
|
60
|
+
// If necessary, we can switch to a two-step process where we first create the notification, send it, and update it after it being sent.
|
|
61
|
+
const createdNotifications = await this.notificationService_.create(notificationsToCreate, sharedContext);
|
|
62
|
+
return createdNotifications;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.default = NotificationModuleService;
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, utils_1.InjectManager)("baseRepository_"),
|
|
68
|
+
__param(1, (0, utils_1.MedusaContext)()),
|
|
69
|
+
__metadata("design:type", Function),
|
|
70
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
71
|
+
__metadata("design:returntype", Promise)
|
|
72
|
+
], NotificationModuleService.prototype, "create", null);
|
|
73
|
+
__decorate([
|
|
74
|
+
(0, utils_1.InjectTransactionManager)("baseRepository_"),
|
|
75
|
+
__param(1, (0, utils_1.MedusaContext)()),
|
|
76
|
+
__metadata("design:type", Function),
|
|
77
|
+
__metadata("design:paramtypes", [Array, Object]),
|
|
78
|
+
__metadata("design:returntype", Promise)
|
|
79
|
+
], NotificationModuleService.prototype, "create_", null);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DAL, NotificationTypes } from "@medusajs/types";
|
|
2
|
+
import { NotificationProvider } from "../models";
|
|
3
|
+
import { NotificationProviderRegistrationPrefix } from "../types";
|
|
4
|
+
type InjectedDependencies = {
|
|
5
|
+
notificationProviderRepository: DAL.RepositoryService;
|
|
6
|
+
[key: `${typeof NotificationProviderRegistrationPrefix}${string}`]: NotificationTypes.INotificationProvider;
|
|
7
|
+
};
|
|
8
|
+
declare const NotificationProviderService_base: new <TEntity extends object = any>(container: InjectedDependencies) => import("@medusajs/types").InternalModuleService<TEntity, InjectedDependencies>;
|
|
9
|
+
export default class NotificationProviderService extends NotificationProviderService_base {
|
|
10
|
+
protected readonly notificationProviderRepository_: DAL.RepositoryService<NotificationProvider>;
|
|
11
|
+
protected providersCache: Map<string, NotificationProvider>;
|
|
12
|
+
constructor(container: InjectedDependencies);
|
|
13
|
+
protected retrieveProviderRegistration(providerId: string): NotificationTypes.INotificationProvider;
|
|
14
|
+
getProviderForChannel(channel: string): Promise<NotificationProvider | undefined>;
|
|
15
|
+
send(provider: NotificationProvider, notification: NotificationTypes.ProviderSendNotificationDTO): Promise<NotificationTypes.ProviderSendNotificationResultsDTO>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@medusajs/utils");
|
|
4
|
+
const medusa_core_utils_1 = require("medusa-core-utils");
|
|
5
|
+
const _models_1 = require("../models");
|
|
6
|
+
const _types_1 = require("../types");
|
|
7
|
+
class NotificationProviderService extends utils_1.ModulesSdkUtils.internalModuleServiceFactory(_models_1.NotificationProvider) {
|
|
8
|
+
constructor(container) {
|
|
9
|
+
super(container);
|
|
10
|
+
this.notificationProviderRepository_ =
|
|
11
|
+
container.notificationProviderRepository;
|
|
12
|
+
}
|
|
13
|
+
retrieveProviderRegistration(providerId) {
|
|
14
|
+
try {
|
|
15
|
+
return this.__container__[`${_types_1.NotificationProviderRegistrationPrefix}${providerId}`];
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
throw new medusa_core_utils_1.MedusaError(medusa_core_utils_1.MedusaError.Types.NOT_FOUND, `Could not find a notification provider with id: ${providerId}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async getProviderForChannel(channel) {
|
|
22
|
+
if (!this.providersCache) {
|
|
23
|
+
const providers = await this.notificationProviderRepository_.find();
|
|
24
|
+
this.providersCache = new Map(providers.flatMap((provider) => provider.channels.map((c) => [c, provider])));
|
|
25
|
+
}
|
|
26
|
+
return this.providersCache.get(channel);
|
|
27
|
+
}
|
|
28
|
+
async send(provider, notification) {
|
|
29
|
+
const providerHandler = this.retrieveProviderRegistration(provider.id);
|
|
30
|
+
return await providerHandler.send(notification);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.default = NotificationProviderService;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Logger, ModuleProviderExports, ModuleServiceInitializeOptions } from "@medusajs/types";
|
|
2
|
+
export type InitializeModuleInjectableDependencies = {
|
|
3
|
+
logger?: Logger;
|
|
4
|
+
};
|
|
5
|
+
export declare const NotificationIdentifiersRegistrationName = "notification_providers_identifier";
|
|
6
|
+
export declare const NotificationProviderRegistrationPrefix = "np_";
|
|
7
|
+
export type NotificationModuleOptions = Partial<ModuleServiceInitializeOptions> & {
|
|
8
|
+
/**
|
|
9
|
+
* Providers to be registered
|
|
10
|
+
*/
|
|
11
|
+
providers?: {
|
|
12
|
+
/**
|
|
13
|
+
* The module provider to be registered
|
|
14
|
+
*/
|
|
15
|
+
resolve: string | ModuleProviderExports;
|
|
16
|
+
options: {
|
|
17
|
+
/**
|
|
18
|
+
* key value pair of the provider name and the configuration to be passed to the provider constructor
|
|
19
|
+
*/
|
|
20
|
+
config: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotificationProviderRegistrationPrefix = exports.NotificationIdentifiersRegistrationName = void 0;
|
|
4
|
+
exports.NotificationIdentifiersRegistrationName = "notification_providers_identifier";
|
|
5
|
+
exports.NotificationProviderRegistrationPrefix = "np_";
|
|
File without changes
|
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@medusajs/notification",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Medusa Notification module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=16"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/medusajs/medusa",
|
|
16
|
+
"directory": "packages/modules/notification"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"author": "Medusa",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"watch": "tsc --build --watch",
|
|
25
|
+
"watch:test": "tsc --build tsconfig.spec.json --watch",
|
|
26
|
+
"prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json",
|
|
27
|
+
"build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json",
|
|
28
|
+
"test": "jest --runInBand --bail --forceExit --passWithNoTests -- src",
|
|
29
|
+
"test:integration": "jest --forceExit -- integration-tests/**/__tests__/**/*.spec.ts",
|
|
30
|
+
"migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate",
|
|
31
|
+
"migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial -n InitialSetupMigration",
|
|
32
|
+
"migration:create": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create",
|
|
33
|
+
"migration:up": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:up",
|
|
34
|
+
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@mikro-orm/cli": "5.9.7",
|
|
38
|
+
"cross-env": "^5.2.1",
|
|
39
|
+
"jest": "^29.6.3",
|
|
40
|
+
"medusa-test-utils": "^1.1.44",
|
|
41
|
+
"rimraf": "^3.0.2",
|
|
42
|
+
"ts-jest": "^29.1.1",
|
|
43
|
+
"ts-node": "^10.9.1",
|
|
44
|
+
"tsc-alias": "^1.8.6",
|
|
45
|
+
"typescript": "^5.1.6"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@medusajs/modules-sdk": "^1.12.11",
|
|
49
|
+
"@medusajs/types": "^1.11.16",
|
|
50
|
+
"@medusajs/utils": "^1.11.9",
|
|
51
|
+
"@mikro-orm/core": "5.9.7",
|
|
52
|
+
"@mikro-orm/migrations": "5.9.7",
|
|
53
|
+
"@mikro-orm/postgresql": "5.9.7",
|
|
54
|
+
"awilix": "^8.0.0",
|
|
55
|
+
"dotenv": "^16.4.5",
|
|
56
|
+
"knex": "2.4.2"
|
|
57
|
+
}
|
|
58
|
+
}
|