@carlonicora/nestjs-neo4jsonapi 8.9.0 → 8.10.1
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/dist/common/constants/core-module.ids.d.ts +1 -0
- package/dist/common/constants/core-module.ids.d.ts.map +1 -1
- package/dist/common/constants/core-module.ids.js +1 -0
- package/dist/common/constants/core-module.ids.js.map +1 -1
- package/dist/config/enums/queue.id.d.ts +1 -0
- package/dist/config/enums/queue.id.d.ts.map +1 -1
- package/dist/config/enums/queue.id.js +1 -0
- package/dist/config/enums/queue.id.js.map +1 -1
- package/dist/core/email/templates/en/emailBlast.hbs +24 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.d.ts.map +1 -1
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.js +1 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.js.map +1 -1
- package/dist/foundations/email-automation/index.d.ts +1 -0
- package/dist/foundations/email-automation/index.d.ts.map +1 -1
- package/dist/foundations/email-automation/index.js +2 -0
- package/dist/foundations/email-automation/index.js.map +1 -1
- package/dist/foundations/email-automation/services/email-automation-renderer.service.d.ts.map +1 -1
- package/dist/foundations/email-automation/services/email-automation-renderer.service.js +2 -12
- package/dist/foundations/email-automation/services/email-automation-renderer.service.js.map +1 -1
- package/dist/foundations/email-automation/utils/email-markdown.renderer.d.ts +20 -0
- package/dist/foundations/email-automation/utils/email-markdown.renderer.d.ts.map +1 -0
- package/dist/foundations/email-automation/utils/email-markdown.renderer.js +63 -0
- package/dist/foundations/email-automation/utils/email-markdown.renderer.js.map +1 -0
- package/dist/foundations/email-blast/controllers/email-blast.controller.d.ts +30 -0
- package/dist/foundations/email-blast/controllers/email-blast.controller.d.ts.map +1 -0
- package/dist/foundations/email-blast/controllers/email-blast.controller.js +171 -0
- package/dist/foundations/email-blast/controllers/email-blast.controller.js.map +1 -0
- package/dist/foundations/email-blast/controllers/email-blast.public.controller.d.ts +24 -0
- package/dist/foundations/email-blast/controllers/email-blast.public.controller.d.ts.map +1 -0
- package/dist/foundations/email-blast/controllers/email-blast.public.controller.js +61 -0
- package/dist/foundations/email-blast/controllers/email-blast.public.controller.js.map +1 -0
- package/dist/foundations/email-blast/cron/email-blast-recovery.cron.d.ts +15 -0
- package/dist/foundations/email-blast/cron/email-blast-recovery.cron.d.ts.map +1 -0
- package/dist/foundations/email-blast/cron/email-blast-recovery.cron.js +50 -0
- package/dist/foundations/email-blast/cron/email-blast-recovery.cron.js.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast-unsubscribe.dto.d.ts +7 -0
- package/dist/foundations/email-blast/dtos/email-blast-unsubscribe.dto.d.ts.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast-unsubscribe.dto.js +35 -0
- package/dist/foundations/email-blast/dtos/email-blast-unsubscribe.dto.js.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast.patch.dto.d.ts +14 -0
- package/dist/foundations/email-blast/dtos/email-blast.patch.dto.d.ts.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast.patch.dto.js +63 -0
- package/dist/foundations/email-blast/dtos/email-blast.patch.dto.js.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast.post.dto.d.ts +14 -0
- package/dist/foundations/email-blast/dtos/email-blast.post.dto.d.ts.map +1 -0
- package/dist/foundations/email-blast/dtos/email-blast.post.dto.js +63 -0
- package/dist/foundations/email-blast/dtos/email-blast.post.dto.js.map +1 -0
- package/dist/foundations/email-blast/email-blast.constants.d.ts +34 -0
- package/dist/foundations/email-blast/email-blast.constants.d.ts.map +1 -0
- package/dist/foundations/email-blast/email-blast.constants.js +49 -0
- package/dist/foundations/email-blast/email-blast.constants.js.map +1 -0
- package/dist/foundations/email-blast/email-blast.module.d.ts +21 -0
- package/dist/foundations/email-blast/email-blast.module.d.ts.map +1 -0
- package/dist/foundations/email-blast/email-blast.module.js +101 -0
- package/dist/foundations/email-blast/email-blast.module.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.d.ts +10 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.js +15 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.meta.d.ts +6 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.meta.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.meta.js +13 -0
- package/dist/foundations/email-blast/entities/email-blast-audience.meta.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.d.ts +31 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.js +34 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.meta.d.ts +3 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.meta.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.meta.js +10 -0
- package/dist/foundations/email-blast/entities/email-blast-delivery.meta.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast.d.ts +16 -0
- package/dist/foundations/email-blast/entities/email-blast.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast.js +25 -0
- package/dist/foundations/email-blast/entities/email-blast.js.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast.meta.d.ts +13 -0
- package/dist/foundations/email-blast/entities/email-blast.meta.d.ts.map +1 -0
- package/dist/foundations/email-blast/entities/email-blast.meta.js +20 -0
- package/dist/foundations/email-blast/entities/email-blast.meta.js.map +1 -0
- package/dist/foundations/email-blast/index.d.ts +20 -0
- package/dist/foundations/email-blast/index.d.ts.map +1 -0
- package/dist/foundations/email-blast/index.js +44 -0
- package/dist/foundations/email-blast/index.js.map +1 -0
- package/dist/foundations/email-blast/processors/email-blast.processor.d.ts +23 -0
- package/dist/foundations/email-blast/processors/email-blast.processor.d.ts.map +1 -0
- package/dist/foundations/email-blast/processors/email-blast.processor.js +80 -0
- package/dist/foundations/email-blast/processors/email-blast.processor.js.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast-delivery.repository.d.ts +98 -0
- package/dist/foundations/email-blast/repositories/email-blast-delivery.repository.d.ts.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast-delivery.repository.js +273 -0
- package/dist/foundations/email-blast/repositories/email-blast-delivery.repository.js.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast.admin.repository.d.ts +19 -0
- package/dist/foundations/email-blast/repositories/email-blast.admin.repository.d.ts.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast.admin.repository.js +63 -0
- package/dist/foundations/email-blast/repositories/email-blast.admin.repository.js.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast.repository.d.ts +29 -0
- package/dist/foundations/email-blast/repositories/email-blast.repository.d.ts.map +1 -0
- package/dist/foundations/email-blast/repositories/email-blast.repository.js +97 -0
- package/dist/foundations/email-blast/repositories/email-blast.repository.js.map +1 -0
- package/dist/foundations/email-blast/services/email-blast-delivery.service.d.ts +79 -0
- package/dist/foundations/email-blast/services/email-blast-delivery.service.d.ts.map +1 -0
- package/dist/foundations/email-blast/services/email-blast-delivery.service.js +206 -0
- package/dist/foundations/email-blast/services/email-blast-delivery.service.js.map +1 -0
- package/dist/foundations/email-blast/services/email-blast.service.d.ts +57 -0
- package/dist/foundations/email-blast/services/email-blast.service.d.ts.map +1 -0
- package/dist/foundations/email-blast/services/email-blast.service.js +134 -0
- package/dist/foundations/email-blast/services/email-blast.service.js.map +1 -0
- package/dist/foundations/email-blast/utils/email-blast.renderer.d.ts +29 -0
- package/dist/foundations/email-blast/utils/email-blast.renderer.d.ts.map +1 -0
- package/dist/foundations/email-blast/utils/email-blast.renderer.js +70 -0
- package/dist/foundations/email-blast/utils/email-blast.renderer.js.map +1 -0
- package/dist/foundations/foundations.modules.d.ts.map +1 -1
- package/dist/foundations/foundations.modules.js +3 -0
- package/dist/foundations/foundations.modules.js.map +1 -1
- package/dist/foundations/index.d.ts +1 -0
- package/dist/foundations/index.d.ts.map +1 -1
- package/dist/foundations/index.js +1 -0
- package/dist/foundations/index.js.map +1 -1
- package/dist/foundations/s3/services/s3.service.d.ts.map +1 -1
- package/dist/foundations/s3/services/s3.service.js +17 -0
- package/dist/foundations/s3/services/s3.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,273 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EmailBlastDeliveryRepository = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const neo4j_driver_1 = require("neo4j-driver");
|
|
15
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
16
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
17
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
18
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
19
|
+
const email_blast_constants_1 = require("../email-blast.constants");
|
|
20
|
+
const email_blast_delivery_1 = require("../entities/email-blast-delivery");
|
|
21
|
+
const email_blast_delivery_meta_1 = require("../entities/email-blast-delivery.meta");
|
|
22
|
+
const email_blast_meta_1 = require("../entities/email-blast.meta");
|
|
23
|
+
/** Alias of the blast whose counter a delivery write raises (never the return statement's alias). */
|
|
24
|
+
const COUNTED_BLAST = "countedBlast";
|
|
25
|
+
/**
|
|
26
|
+
* Every guarded write that changes a counter takes the delivery's write lock first and re-checks
|
|
27
|
+
* its guard after it (SET _lock / REMOVE _lock / WITH / WHERE), so two concurrent calls never both
|
|
28
|
+
* pass. The blast's lock is taken before its counter is read, so concurrent increments never lose
|
|
29
|
+
* one. A counter moves in the same query as the status it counts.
|
|
30
|
+
*/
|
|
31
|
+
let EmailBlastDeliveryRepository = class EmailBlastDeliveryRepository extends abstract_repository_1.AbstractRepository {
|
|
32
|
+
constructor(neo4j, securityService, clsService) {
|
|
33
|
+
super(neo4j, securityService, clsService);
|
|
34
|
+
this.descriptor = email_blast_delivery_1.EmailBlastDeliveryDescriptor;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds the recipient list of a blast once: one pending delivery per eligible, distinct
|
|
38
|
+
* lower-cased email, and the blast's recipientCount (0 when nobody is eligible).
|
|
39
|
+
*
|
|
40
|
+
* Owner-approved exception (Carlo, 2026-10-27, decision D-1 of the email-blasts plan):
|
|
41
|
+
* - Only caller: EmailBlastDeliveryService.processBlast in the worker job, never an HTTP request.
|
|
42
|
+
* - The recipients are every company's users, so the query reads User nodes across companies
|
|
43
|
+
* instead of buildDefaultMatch(), which scopes to one company.
|
|
44
|
+
* - initQuery() is not used: the worker CLS carries no company, and the query needs no
|
|
45
|
+
* request alias. The blast is matched by an explicit id parameter.
|
|
46
|
+
* - Deliveries store userId as a field, never an edge to User.
|
|
47
|
+
* - validateRelatedNodes is not called: the only relationship target is the blast, matched by
|
|
48
|
+
* id in the same query, and for a tenancy "internal" target with no company in context the
|
|
49
|
+
* check is existence only.
|
|
50
|
+
* - The write returns no node, so it runs through executeInTransaction.
|
|
51
|
+
* - A second run does nothing: the blast's write lock is taken first, then the query stops
|
|
52
|
+
* when the blast already has a delivery.
|
|
53
|
+
*/
|
|
54
|
+
async createSnapshot(params) {
|
|
55
|
+
const blast = email_blast_meta_1.emailBlastMeta.nodeName;
|
|
56
|
+
const delivery = email_blast_delivery_meta_1.emailBlastDeliveryMeta.nodeName;
|
|
57
|
+
const query = `
|
|
58
|
+
MATCH (${blast}:${email_blast_meta_1.emailBlastMeta.labelName} {id: $blastId})
|
|
59
|
+
SET ${blast}._lock = true
|
|
60
|
+
REMOVE ${blast}._lock
|
|
61
|
+
WITH ${blast}
|
|
62
|
+
WHERE NOT EXISTS {
|
|
63
|
+
MATCH (${blast})-[:HAS_DELIVERY]->(:${email_blast_delivery_meta_1.emailBlastDeliveryMeta.labelName})
|
|
64
|
+
}
|
|
65
|
+
CALL {
|
|
66
|
+
${email_blast_constants_1.ELIGIBLE_RECIPIENTS_CYPHER}
|
|
67
|
+
RETURN collect({ email: email, recipient: recipient }) AS rows
|
|
68
|
+
}
|
|
69
|
+
FOREACH (row IN rows |
|
|
70
|
+
CREATE (${delivery}:${email_blast_delivery_meta_1.emailBlastDeliveryMeta.labelName} {
|
|
71
|
+
id: randomUUID(),
|
|
72
|
+
email: row.email,
|
|
73
|
+
name: row.recipient.name,
|
|
74
|
+
userId: row.recipient.id,
|
|
75
|
+
status: "pending",
|
|
76
|
+
unsubscribeToken: randomUUID(),
|
|
77
|
+
createdAt: datetime(),
|
|
78
|
+
updatedAt: datetime()
|
|
79
|
+
})
|
|
80
|
+
CREATE (${blast})-[:HAS_DELIVERY]->(${delivery})
|
|
81
|
+
)
|
|
82
|
+
SET ${blast}.recipientCount = size(rows),
|
|
83
|
+
${blast}.updatedAt = datetime()
|
|
84
|
+
`;
|
|
85
|
+
await this.neo4j.executeInTransaction([{ query, params: { blastId: params.blastId } }]);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Claims up to `limit` pending deliveries of a blast (pending to sending), oldest first.
|
|
89
|
+
*/
|
|
90
|
+
async claimBatch(params) {
|
|
91
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
92
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
93
|
+
query.queryParams = {
|
|
94
|
+
...query.queryParams,
|
|
95
|
+
blastId: params.blastId,
|
|
96
|
+
limit: (0, neo4j_driver_1.int)(params.limit),
|
|
97
|
+
claimedAt: params.claimedAt.toISOString(),
|
|
98
|
+
};
|
|
99
|
+
query.query += `
|
|
100
|
+
${this.buildDefaultMatch()}
|
|
101
|
+
WHERE ${nodeName}.status = "pending"
|
|
102
|
+
AND EXISTS {
|
|
103
|
+
MATCH (${nodeName})<-[:HAS_DELIVERY]-(:${email_blast_meta_1.emailBlastMeta.labelName} {id: $blastId})
|
|
104
|
+
}
|
|
105
|
+
WITH ${nodeName}
|
|
106
|
+
ORDER BY ${nodeName}.createdAt ASC
|
|
107
|
+
LIMIT $limit
|
|
108
|
+
SET ${nodeName}._lock = true
|
|
109
|
+
REMOVE ${nodeName}._lock
|
|
110
|
+
WITH ${nodeName}
|
|
111
|
+
WHERE ${nodeName}.status = "pending"
|
|
112
|
+
SET ${nodeName}.status = "sending",
|
|
113
|
+
${nodeName}.claimedAt = datetime($claimedAt),
|
|
114
|
+
${nodeName}.updatedAt = datetime()
|
|
115
|
+
WITH ${nodeName}
|
|
116
|
+
${this.buildReturnStatement()}
|
|
117
|
+
`;
|
|
118
|
+
return (await this.neo4j.writeAndReturnMany(query)) ?? [];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A claimed delivery was sent: sending to sent, and the blast's sentCount + 1.
|
|
122
|
+
*/
|
|
123
|
+
async markSent(params) {
|
|
124
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
125
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
126
|
+
query.queryParams = {
|
|
127
|
+
...query.queryParams,
|
|
128
|
+
searchValue: params.id,
|
|
129
|
+
sentAt: params.sentAt.toISOString(),
|
|
130
|
+
};
|
|
131
|
+
query.query += `
|
|
132
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
133
|
+
SET ${nodeName}._lock = true
|
|
134
|
+
REMOVE ${nodeName}._lock
|
|
135
|
+
WITH ${nodeName}
|
|
136
|
+
WHERE ${nodeName}.status = "sending"
|
|
137
|
+
${this._lockCountedBlast()}
|
|
138
|
+
SET ${nodeName}.status = "sent",
|
|
139
|
+
${nodeName}.sentAt = datetime($sentAt),
|
|
140
|
+
${nodeName}.updatedAt = datetime(),
|
|
141
|
+
${COUNTED_BLAST}.sentCount = coalesce(${COUNTED_BLAST}.sentCount, 0) + 1
|
|
142
|
+
WITH ${nodeName}
|
|
143
|
+
${this.buildReturnStatement()}
|
|
144
|
+
`;
|
|
145
|
+
await this.neo4j.writeOne(query);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A claimed delivery failed to send: sending to failed (never retried), and the blast's
|
|
149
|
+
* failedCount + 1.
|
|
150
|
+
*/
|
|
151
|
+
async markFailed(params) {
|
|
152
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
153
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
154
|
+
query.queryParams = { ...query.queryParams, searchValue: params.id, error: params.error };
|
|
155
|
+
query.query += `
|
|
156
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
157
|
+
SET ${nodeName}._lock = true
|
|
158
|
+
REMOVE ${nodeName}._lock
|
|
159
|
+
WITH ${nodeName}
|
|
160
|
+
WHERE ${nodeName}.status = "sending"
|
|
161
|
+
${this._lockCountedBlast()}
|
|
162
|
+
SET ${nodeName}.status = "failed",
|
|
163
|
+
${nodeName}.error = $error,
|
|
164
|
+
${nodeName}.updatedAt = datetime(),
|
|
165
|
+
${COUNTED_BLAST}.failedCount = coalesce(${COUNTED_BLAST}.failedCount, 0) + 1
|
|
166
|
+
WITH ${nodeName}
|
|
167
|
+
${this.buildReturnStatement()}
|
|
168
|
+
`;
|
|
169
|
+
await this.neo4j.writeOne(query);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A claimed delivery whose address opted out after the snapshot: sending to skipped. No counter.
|
|
173
|
+
*/
|
|
174
|
+
async markSkipped(params) {
|
|
175
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
176
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
177
|
+
query.queryParams = { ...query.queryParams, searchValue: params.id };
|
|
178
|
+
query.query += `
|
|
179
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
180
|
+
SET ${nodeName}._lock = true
|
|
181
|
+
REMOVE ${nodeName}._lock
|
|
182
|
+
WITH ${nodeName}
|
|
183
|
+
WHERE ${nodeName}.status = "sending"
|
|
184
|
+
SET ${nodeName}.status = "skipped",
|
|
185
|
+
${nodeName}.updatedAt = datetime()
|
|
186
|
+
WITH ${nodeName}
|
|
187
|
+
${this.buildReturnStatement()}
|
|
188
|
+
`;
|
|
189
|
+
await this.neo4j.writeOne(query);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Every delivery claimed before `claimedBefore` and still sending goes to failed (never resent),
|
|
193
|
+
* and its blast's failedCount rises by one per delivery, in one query.
|
|
194
|
+
*/
|
|
195
|
+
async failStuck(params) {
|
|
196
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
197
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
198
|
+
query.queryParams = { ...query.queryParams, claimedBefore: params.claimedBefore.toISOString() };
|
|
199
|
+
query.query += `
|
|
200
|
+
${this.buildDefaultMatch()}
|
|
201
|
+
WHERE ${nodeName}.status = "sending"
|
|
202
|
+
AND ${nodeName}.claimedAt < datetime($claimedBefore)
|
|
203
|
+
SET ${nodeName}._lock = true
|
|
204
|
+
REMOVE ${nodeName}._lock
|
|
205
|
+
WITH ${nodeName}
|
|
206
|
+
WHERE ${nodeName}.status = "sending"
|
|
207
|
+
AND ${nodeName}.claimedAt < datetime($claimedBefore)
|
|
208
|
+
${this._lockCountedBlast()}
|
|
209
|
+
SET ${nodeName}.status = "failed",
|
|
210
|
+
${nodeName}.error = "stuck",
|
|
211
|
+
${nodeName}.updatedAt = datetime(),
|
|
212
|
+
${COUNTED_BLAST}.failedCount = coalesce(${COUNTED_BLAST}.failedCount, 0) + 1
|
|
213
|
+
WITH ${nodeName}
|
|
214
|
+
${this.buildReturnStatement()}
|
|
215
|
+
`;
|
|
216
|
+
await this.neo4j.writeAndReturnMany(query);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The delivery an unsubscribe link belongs to, with its blast.
|
|
220
|
+
*/
|
|
221
|
+
async findByUnsubscribeToken(params) {
|
|
222
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
223
|
+
query.queryParams = { ...query.queryParams, searchValue: params.token };
|
|
224
|
+
query.query += `
|
|
225
|
+
${this.buildDefaultMatch({ searchField: "unsubscribeToken" })}
|
|
226
|
+
${this.buildReturnStatement()}
|
|
227
|
+
`;
|
|
228
|
+
return this.neo4j.readOne(query);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Stamps the first unsubscribe of a delivery and raises the blast's unsubscribedCount, once.
|
|
232
|
+
* True when this call was the one that matched.
|
|
233
|
+
*/
|
|
234
|
+
async markUnsubscribed(params) {
|
|
235
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
236
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_delivery_1.EmailBlastDeliveryDescriptor.model });
|
|
237
|
+
query.queryParams = {
|
|
238
|
+
...query.queryParams,
|
|
239
|
+
searchValue: params.id,
|
|
240
|
+
unsubscribedAt: params.unsubscribedAt.toISOString(),
|
|
241
|
+
};
|
|
242
|
+
query.query += `
|
|
243
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
244
|
+
SET ${nodeName}._lock = true
|
|
245
|
+
REMOVE ${nodeName}._lock
|
|
246
|
+
WITH ${nodeName}
|
|
247
|
+
WHERE ${nodeName}.unsubscribedAt IS NULL
|
|
248
|
+
${this._lockCountedBlast()}
|
|
249
|
+
SET ${nodeName}.unsubscribedAt = datetime($unsubscribedAt),
|
|
250
|
+
${nodeName}.updatedAt = datetime(),
|
|
251
|
+
${COUNTED_BLAST}.unsubscribedCount = coalesce(${COUNTED_BLAST}.unsubscribedCount, 0) + 1
|
|
252
|
+
WITH ${nodeName}
|
|
253
|
+
${this.buildReturnStatement()}
|
|
254
|
+
`;
|
|
255
|
+
return !!(await this.neo4j.writeOne(query));
|
|
256
|
+
}
|
|
257
|
+
/** Binds the delivery's blast as COUNTED_BLAST and takes its write lock before a counter is read. */
|
|
258
|
+
_lockCountedBlast() {
|
|
259
|
+
const { nodeName } = email_blast_delivery_meta_1.emailBlastDeliveryMeta;
|
|
260
|
+
return `
|
|
261
|
+
MATCH (${nodeName})<-[:HAS_DELIVERY]-(${COUNTED_BLAST}:${email_blast_meta_1.emailBlastMeta.labelName})
|
|
262
|
+
SET ${COUNTED_BLAST}._lock = true
|
|
263
|
+
REMOVE ${COUNTED_BLAST}._lock
|
|
264
|
+
WITH ${nodeName}, ${COUNTED_BLAST}
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
exports.EmailBlastDeliveryRepository = EmailBlastDeliveryRepository;
|
|
269
|
+
exports.EmailBlastDeliveryRepository = EmailBlastDeliveryRepository = __decorate([
|
|
270
|
+
(0, common_1.Injectable)(),
|
|
271
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
272
|
+
], EmailBlastDeliveryRepository);
|
|
273
|
+
//# sourceMappingURL=email-blast-delivery.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast-delivery.repository.js","sourceRoot":"","sources":["../../../../src/foundations/email-blast/repositories/email-blast-delivery.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,+CAAmC;AACnC,2CAAwC;AACxC,2FAAuF;AACvF,8EAA0E;AAC1E,uFAAmF;AACnF,oEAAsE;AACtE,2EAAoG;AACpG,qFAA+E;AAC/E,mEAA8D;AAE9D,qGAAqG;AACrG,MAAM,aAAa,GAAG,cAAc,CAAC;AAErC;;;;;GAKG;AAEI,IAAM,4BAA4B,GAAlC,MAAM,4BAA6B,SAAQ,wCAGjD;IAGC,YAAY,KAAmB,EAAE,eAAgC,EAAE,UAAsB;QACvF,KAAK,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAHzB,eAAU,GAAG,mDAA4B,CAAC;IAI7D,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,cAAc,CAAC,MAA2B;QAC9C,MAAM,KAAK,GAAG,iCAAc,CAAC,QAAQ,CAAC;QACtC,MAAM,QAAQ,GAAG,kDAAsB,CAAC,QAAQ,CAAC;QAEjD,MAAM,KAAK,GAAG;eACH,KAAK,IAAI,iCAAc,CAAC,SAAS;YACpC,KAAK;eACF,KAAK;aACP,KAAK;;iBAED,KAAK,wBAAwB,kDAAsB,CAAC,SAAS;;;UAGpE,kDAA0B;;;;kBAIlB,QAAQ,IAAI,kDAAsB,CAAC,SAAS;;;;;;;;;;kBAU5C,KAAK,uBAAuB,QAAQ;;YAE1C,KAAK;YACL,KAAK;KACZ,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1F,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,MAA2D;QAC1E,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG;YAClB,GAAG,KAAK,CAAC,WAAW;YACpB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,IAAA,kBAAG,EAAC,MAAM,CAAC,KAAK,CAAC;YACxB,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE;SAC1C,CAAC;QAEF,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,EAAE;cAClB,QAAQ;;iBAEL,QAAQ,wBAAwB,iCAAc,CAAC,SAAS;;aAE5D,QAAQ;iBACJ,QAAQ;;YAEb,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;YACV,QAAQ;YACR,QAAQ;YACR,QAAQ;aACP,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAoC;QACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG;YAClB,GAAG,KAAK,CAAC,WAAW;YACpB,WAAW,EAAE,MAAM,CAAC,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE;SACpC,CAAC;QAEF,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;QACd,IAAI,CAAC,iBAAiB,EAAE;YACpB,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,aAAa,yBAAyB,aAAa;aAClD,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,MAAqC;QACpD,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAE1F,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;QACd,IAAI,CAAC,iBAAiB,EAAE;YACpB,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,aAAa,2BAA2B,aAAa;aACpD,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,MAAsB;QACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;QAErE,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;YACV,QAAQ;YACR,QAAQ;aACP,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CAAC,MAA+B;QAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC;QAEhG,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,EAAE;cAClB,QAAQ;YACV,QAAQ;YACR,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;YACV,QAAQ;QACZ,IAAI,CAAC,iBAAiB,EAAE;YACpB,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,aAAa,2BAA2B,aAAa;aACpD,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,sBAAsB,CAAC,MAAyB;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAExE,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAC3D,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,gBAAgB,CAAC,MAA4C;QACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,mDAA4B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvF,KAAK,CAAC,WAAW,GAAG;YAClB,GAAG,KAAK,CAAC,WAAW;YACpB,WAAW,EAAE,MAAM,CAAC,EAAE;YACtB,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE;SACpD,CAAC;QAEF,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;QACd,IAAI,CAAC,iBAAiB,EAAE;YACpB,QAAQ;YACR,QAAQ;YACR,aAAa,iCAAiC,aAAa;aAC1D,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,qGAAqG;IAC7F,iBAAiB;QACvB,MAAM,EAAE,QAAQ,EAAE,GAAG,kDAAsB,CAAC;QAC5C,OAAO;eACI,QAAQ,uBAAuB,aAAa,IAAI,iCAAc,CAAC,SAAS;YAC3E,aAAa;eACV,aAAa;aACf,QAAQ,KAAK,aAAa;KAClC,CAAC;IACJ,CAAC;CACF,CAAA;AAhRY,oEAA4B;uCAA5B,4BAA4B;IADxC,IAAA,mBAAU,GAAE;qCAOQ,4BAAY,EAAmB,kCAAe,EAAc,uBAAU;GAN9E,4BAA4B,CAgRxC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ClsService } from "nestjs-cls";
|
|
2
|
+
import { AbstractRepository } from "../../../core/neo4j/abstracts/abstract.repository";
|
|
3
|
+
import { Neo4jService } from "../../../core/neo4j/services/neo4j.service";
|
|
4
|
+
import { SecurityService } from "../../../core/security/services/security.service";
|
|
5
|
+
import { EmailBlastAudience, EmailBlastAudienceDescriptor } from "../entities/email-blast-audience";
|
|
6
|
+
export declare class EmailBlastAdminRepository extends AbstractRepository<EmailBlastAudience, typeof EmailBlastAudienceDescriptor.relationships> {
|
|
7
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<EmailBlastAudience, {}>;
|
|
8
|
+
constructor(neo4j: Neo4jService, securityService: SecurityService, clsService: ClsService);
|
|
9
|
+
/**
|
|
10
|
+
* How many distinct addresses a blast would reach right now (the same eligibility block the
|
|
11
|
+
* snapshot uses). `id` is the blast id.
|
|
12
|
+
*/
|
|
13
|
+
countEligibleRecipients(params: {
|
|
14
|
+
blastId: string;
|
|
15
|
+
}): Promise<EmailBlastAudience>;
|
|
16
|
+
/** Seeds a query with only the parameters it needs: none (never the caller's companyId). */
|
|
17
|
+
private _adminQuery;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=email-blast.admin.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast.admin.repository.d.ts","sourceRoot":"","sources":["../../../../src/foundations/email-blast/repositories/email-blast.admin.repository.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AAEnF,OAAO,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAIpG,qBACa,yBAA0B,SAAQ,kBAAkB,CAC/D,kBAAkB,EAClB,OAAO,4BAA4B,CAAC,aAAa,CAClD;IAEC,SAAS,CAAC,QAAQ,CAAC,UAAU,8DAAgC;gBAEjD,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;IAIzF;;;OAGG;IACG,uBAAuB,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiBvF,4FAA4F;IAC5F,OAAO,CAAC,WAAW;CAGpB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EmailBlastAdminRepository = void 0;
|
|
13
|
+
// nja-lint-ignore-file: cross-tenant administrative read repository.
|
|
14
|
+
//
|
|
15
|
+
// The recipient count of a blast covers every company's users, so buildDefaultMatch(), which
|
|
16
|
+
// scopes to the caller's company, would undercount; initQuery() is skipped for the same reason
|
|
17
|
+
// (it prepends the caller's company MATCH). The query returns one scalar count, which
|
|
18
|
+
// readOne()/readMany() cannot map. Only EmailBlastController.findAudience and .send reach it,
|
|
19
|
+
// gated by JwtAuthGuard and Roles(SystemRoles.Administrator).
|
|
20
|
+
//
|
|
21
|
+
// Queries seed themselves from _adminQuery(); no query here binds a parameter named companyId.
|
|
22
|
+
const common_1 = require("@nestjs/common");
|
|
23
|
+
const neo4j_driver_1 = require("neo4j-driver");
|
|
24
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
25
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
26
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
27
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
28
|
+
const email_blast_constants_1 = require("../email-blast.constants");
|
|
29
|
+
const email_blast_audience_1 = require("../entities/email-blast-audience");
|
|
30
|
+
let EmailBlastAdminRepository = class EmailBlastAdminRepository extends abstract_repository_1.AbstractRepository {
|
|
31
|
+
constructor(neo4j, securityService, clsService) {
|
|
32
|
+
super(neo4j, securityService, clsService);
|
|
33
|
+
// Only satisfies the base class: nothing here goes through the inherited CRUD.
|
|
34
|
+
this.descriptor = email_blast_audience_1.EmailBlastAudienceDescriptor;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* How many distinct addresses a blast would reach right now (the same eligibility block the
|
|
38
|
+
* snapshot uses). `id` is the blast id.
|
|
39
|
+
*/
|
|
40
|
+
async countEligibleRecipients(params) {
|
|
41
|
+
const seed = this._adminQuery();
|
|
42
|
+
seed.query = `
|
|
43
|
+
${email_blast_constants_1.ELIGIBLE_RECIPIENTS_CYPHER}
|
|
44
|
+
RETURN count(email) AS recipientCount
|
|
45
|
+
`;
|
|
46
|
+
const result = await this.neo4j.read(seed.query, seed.queryParams);
|
|
47
|
+
const raw = result.records[0]?.get("recipientCount");
|
|
48
|
+
return {
|
|
49
|
+
id: params.blastId,
|
|
50
|
+
recipientCount: (0, neo4j_driver_1.isInt)(raw) ? raw.toNumber() : Number(raw ?? 0),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Seeds a query with only the parameters it needs: none (never the caller's companyId). */
|
|
54
|
+
_adminQuery() {
|
|
55
|
+
return { query: "", queryParams: {} };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.EmailBlastAdminRepository = EmailBlastAdminRepository;
|
|
59
|
+
exports.EmailBlastAdminRepository = EmailBlastAdminRepository = __decorate([
|
|
60
|
+
(0, common_1.Injectable)(),
|
|
61
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
62
|
+
], EmailBlastAdminRepository);
|
|
63
|
+
//# sourceMappingURL=email-blast.admin.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast.admin.repository.js","sourceRoot":"","sources":["../../../../src/foundations/email-blast/repositories/email-blast.admin.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qEAAqE;AACrE,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,sFAAsF;AACtF,8FAA8F;AAC9F,8DAA8D;AAC9D,EAAE;AACF,+FAA+F;AAC/F,2CAA4C;AAC5C,+CAAqC;AACrC,2CAAwC;AACxC,2FAAuF;AACvF,8EAA0E;AAC1E,uFAAmF;AACnF,oEAAsE;AACtE,2EAAoG;AAK7F,IAAM,yBAAyB,GAA/B,MAAM,yBAA0B,SAAQ,wCAG9C;IAIC,YAAY,KAAmB,EAAE,eAAgC,EAAE,UAAsB;QACvF,KAAK,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAJ5C,+EAA+E;QAC5D,eAAU,GAAG,mDAA4B,CAAC;IAI7D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,uBAAuB,CAAC,MAA2B;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAEhC,IAAI,CAAC,KAAK,GAAG;QACT,kDAA0B;;KAE7B,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,MAAM,GAAG,GAAI,MAAM,CAAC,OAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAEhE,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,OAAO;YAClB,cAAc,EAAE,IAAA,oBAAK,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;SACzC,CAAC;IAC1B,CAAC;IAED,4FAA4F;IACpF,WAAW;QACjB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACxC,CAAC;CACF,CAAA;AApCY,8DAAyB;oCAAzB,yBAAyB;IADrC,IAAA,mBAAU,GAAE;qCAQQ,4BAAY,EAAmB,kCAAe,EAAc,uBAAU;GAP9E,yBAAyB,CAoCrC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ClsService } from "nestjs-cls";
|
|
2
|
+
import { AbstractRepository } from "../../../core/neo4j/abstracts/abstract.repository";
|
|
3
|
+
import { Neo4jService } from "../../../core/neo4j/services/neo4j.service";
|
|
4
|
+
import { SecurityService } from "../../../core/security/services/security.service";
|
|
5
|
+
import { EmailBlast, EmailBlastDescriptor } from "../entities/email-blast";
|
|
6
|
+
export declare class EmailBlastRepository extends AbstractRepository<EmailBlast, typeof EmailBlastDescriptor.relationships> {
|
|
7
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<EmailBlast, {}>;
|
|
8
|
+
constructor(neo4j: Neo4jService, securityService: SecurityService, clsService: ClsService);
|
|
9
|
+
/**
|
|
10
|
+
* Moves a draft to `sending` and stamps `sentAt`. The node's write lock is taken before the
|
|
11
|
+
* draft check, so of two concurrent calls only one matches; null when it is not a draft.
|
|
12
|
+
*/
|
|
13
|
+
markSending(params: {
|
|
14
|
+
id: string;
|
|
15
|
+
sentAt: Date;
|
|
16
|
+
}): Promise<EmailBlast | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Moves a `sending` blast to `sent` once none of its deliveries is `pending` or `sending`.
|
|
19
|
+
* Null when the blast is not done yet (or not sending).
|
|
20
|
+
*/
|
|
21
|
+
markSentIfDone(params: {
|
|
22
|
+
id: string;
|
|
23
|
+
}): Promise<EmailBlast | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Every blast still sending (the recovery cron re-enqueues them).
|
|
26
|
+
*/
|
|
27
|
+
findSending(): Promise<EmailBlast[]>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=email-blast.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast.repository.d.ts","sourceRoot":"","sources":["../../../../src/foundations/email-blast/repositories/email-blast.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAI3E,qBACa,oBAAqB,SAAQ,kBAAkB,CAAC,UAAU,EAAE,OAAO,oBAAoB,CAAC,aAAa,CAAC;IACjH,SAAS,CAAC,QAAQ,CAAC,UAAU,sDAAwB;gBAEzC,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;IAIzF;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0BnF;;;OAGG;IACG,cAAc,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAyBxE;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;CAa3C"}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EmailBlastRepository = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
15
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
16
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
17
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
18
|
+
const email_blast_1 = require("../entities/email-blast");
|
|
19
|
+
const email_blast_delivery_meta_1 = require("../entities/email-blast-delivery.meta");
|
|
20
|
+
const email_blast_meta_1 = require("../entities/email-blast.meta");
|
|
21
|
+
let EmailBlastRepository = class EmailBlastRepository extends abstract_repository_1.AbstractRepository {
|
|
22
|
+
constructor(neo4j, securityService, clsService) {
|
|
23
|
+
super(neo4j, securityService, clsService);
|
|
24
|
+
this.descriptor = email_blast_1.EmailBlastDescriptor;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Moves a draft to `sending` and stamps `sentAt`. The node's write lock is taken before the
|
|
28
|
+
* draft check, so of two concurrent calls only one matches; null when it is not a draft.
|
|
29
|
+
*/
|
|
30
|
+
async markSending(params) {
|
|
31
|
+
const { nodeName } = email_blast_meta_1.emailBlastMeta;
|
|
32
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_1.EmailBlastDescriptor.model });
|
|
33
|
+
query.queryParams = {
|
|
34
|
+
...query.queryParams,
|
|
35
|
+
searchValue: params.id,
|
|
36
|
+
sentAt: params.sentAt.toISOString(),
|
|
37
|
+
};
|
|
38
|
+
query.query += `
|
|
39
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
40
|
+
SET ${nodeName}._lock = true
|
|
41
|
+
REMOVE ${nodeName}._lock
|
|
42
|
+
WITH ${nodeName}
|
|
43
|
+
WHERE ${nodeName}.status = "draft"
|
|
44
|
+
SET ${nodeName}.status = "sending",
|
|
45
|
+
${nodeName}.sentAt = datetime($sentAt),
|
|
46
|
+
${nodeName}.updatedAt = datetime()
|
|
47
|
+
WITH ${nodeName}
|
|
48
|
+
${this.buildReturnStatement()}
|
|
49
|
+
`;
|
|
50
|
+
return this.neo4j.writeOne(query);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Moves a `sending` blast to `sent` once none of its deliveries is `pending` or `sending`.
|
|
54
|
+
* Null when the blast is not done yet (or not sending).
|
|
55
|
+
*/
|
|
56
|
+
async markSentIfDone(params) {
|
|
57
|
+
const { nodeName } = email_blast_meta_1.emailBlastMeta;
|
|
58
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_1.EmailBlastDescriptor.model });
|
|
59
|
+
query.queryParams = { ...query.queryParams, searchValue: params.id };
|
|
60
|
+
query.query += `
|
|
61
|
+
${this.buildDefaultMatch({ searchField: "id" })}
|
|
62
|
+
SET ${nodeName}._lock = true
|
|
63
|
+
REMOVE ${nodeName}._lock
|
|
64
|
+
WITH ${nodeName}
|
|
65
|
+
WHERE ${nodeName}.status = "sending"
|
|
66
|
+
AND NOT EXISTS {
|
|
67
|
+
MATCH (${nodeName})-[:HAS_DELIVERY]->(open:${email_blast_delivery_meta_1.emailBlastDeliveryMeta.labelName})
|
|
68
|
+
WHERE open.status IN ["pending", "sending"]
|
|
69
|
+
}
|
|
70
|
+
SET ${nodeName}.status = "sent",
|
|
71
|
+
${nodeName}.updatedAt = datetime()
|
|
72
|
+
WITH ${nodeName}
|
|
73
|
+
${this.buildReturnStatement()}
|
|
74
|
+
`;
|
|
75
|
+
return this.neo4j.writeOne(query);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Every blast still sending (the recovery cron re-enqueues them).
|
|
79
|
+
*/
|
|
80
|
+
async findSending() {
|
|
81
|
+
const { nodeName } = email_blast_meta_1.emailBlastMeta;
|
|
82
|
+
const query = this.neo4j.initQuery({ serialiser: email_blast_1.EmailBlastDescriptor.model });
|
|
83
|
+
query.query += `
|
|
84
|
+
${this.buildDefaultMatch()}
|
|
85
|
+
WHERE ${nodeName}.status = "sending"
|
|
86
|
+
WITH ${nodeName}
|
|
87
|
+
${this.buildReturnStatement()}
|
|
88
|
+
`;
|
|
89
|
+
return this.neo4j.readMany(query);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
exports.EmailBlastRepository = EmailBlastRepository;
|
|
93
|
+
exports.EmailBlastRepository = EmailBlastRepository = __decorate([
|
|
94
|
+
(0, common_1.Injectable)(),
|
|
95
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
96
|
+
], EmailBlastRepository);
|
|
97
|
+
//# sourceMappingURL=email-blast.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast.repository.js","sourceRoot":"","sources":["../../../../src/foundations/email-blast/repositories/email-blast.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAAwC;AACxC,2FAAuF;AACvF,8EAA0E;AAC1E,uFAAmF;AACnF,yDAA2E;AAC3E,qFAA+E;AAC/E,mEAA8D;AAGvD,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,wCAAyE;IAGjH,YAAY,KAAmB,EAAE,eAAgC,EAAE,UAAsB;QACvF,KAAK,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAHzB,eAAU,GAAG,kCAAoB,CAAC;IAIrD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,MAAoC;QACpD,MAAM,EAAE,QAAQ,EAAE,GAAG,iCAAc,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,kCAAoB,CAAC,KAAK,EAAE,CAAC,CAAC;QAE/E,KAAK,CAAC,WAAW,GAAG;YAClB,GAAG,KAAK,CAAC,WAAW;YACpB,WAAW,EAAE,MAAM,CAAC,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE;SACpC,CAAC;QAEF,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;YACV,QAAQ;YACR,QAAQ;YACR,QAAQ;aACP,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAAC,MAAsB;QACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,iCAAc,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,kCAAoB,CAAC,KAAK,EAAE,CAAC,CAAC;QAE/E,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;QAErE,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACzC,QAAQ;eACL,QAAQ;aACV,QAAQ;cACP,QAAQ;;iBAEL,QAAQ,4BAA4B,kDAAsB,CAAC,SAAS;;;YAGzE,QAAQ;YACR,QAAQ;aACP,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,EAAE,QAAQ,EAAE,GAAG,iCAAc,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,kCAAoB,CAAC,KAAK,EAAE,CAAC,CAAC;QAE/E,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,EAAE;cAClB,QAAQ;aACT,QAAQ;QACb,IAAI,CAAC,oBAAoB,EAAE;KAC9B,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;CACF,CAAA;AAlFY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAIQ,4BAAY,EAAmB,kCAAe,EAAc,uBAAU;GAH9E,oBAAoB,CAkFhC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ConfigService } from "@nestjs/config";
|
|
2
|
+
import { Queue } from "bullmq";
|
|
3
|
+
import { ClsService } from "nestjs-cls";
|
|
4
|
+
import { BaseConfigInterface } from "../../../config/interfaces";
|
|
5
|
+
import { CacheService } from "../../../core/cache/services/cache.service";
|
|
6
|
+
import { EmailService } from "../../../core/email/services/email.service";
|
|
7
|
+
import { JsonApiService } from "../../../core/jsonapi/services/jsonapi.service";
|
|
8
|
+
import { AppLoggingService } from "../../../core/logging/services/logging.service";
|
|
9
|
+
import { AbstractService } from "../../../core/neo4j/abstracts/abstract.service";
|
|
10
|
+
import { EmailAutomationEnrollmentRepository } from "../../email-automation/repositories/email-automation-enrollment.repository";
|
|
11
|
+
import { EmailOptOutRepository } from "../../email-automation/repositories/email-opt-out.repository";
|
|
12
|
+
import { ResponseLanguageService } from "../../user/services/response-language.service";
|
|
13
|
+
import { EmailBlastDelivery, EmailBlastDeliveryDescriptor } from "../entities/email-blast-delivery";
|
|
14
|
+
import { EmailBlastDeliveryRepository } from "../repositories/email-blast-delivery.repository";
|
|
15
|
+
import { EmailBlastRepository } from "../repositories/email-blast.repository";
|
|
16
|
+
/**
|
|
17
|
+
* The whole delivery lifecycle of a blast: the worker send run, the recovery of stuck
|
|
18
|
+
* deliveries and the public unsubscribe.
|
|
19
|
+
*
|
|
20
|
+
* Never twice beats never missing anyone: a delivery is claimed (`pending` → `sending`)
|
|
21
|
+
* before the provider call; a send error marks it `failed` and it is never retried; a
|
|
22
|
+
* delivery left in `sending` by a dead worker is failed by `recoverStuck`, never resent.
|
|
23
|
+
*
|
|
24
|
+
* The worker has no route for `@CacheInvalidate`, so the send run and the recovery
|
|
25
|
+
* invalidate the `email-blasts` cache by hand (owner decision D-7).
|
|
26
|
+
*/
|
|
27
|
+
export declare class EmailBlastDeliveryService extends AbstractService<EmailBlastDelivery, typeof EmailBlastDeliveryDescriptor.relationships> {
|
|
28
|
+
private readonly deliveryRepository;
|
|
29
|
+
private readonly blastRepository;
|
|
30
|
+
private readonly optOutRepository;
|
|
31
|
+
private readonly enrollmentRepository;
|
|
32
|
+
private readonly emailService;
|
|
33
|
+
private readonly responseLanguage;
|
|
34
|
+
private readonly configService;
|
|
35
|
+
private readonly cacheService;
|
|
36
|
+
private readonly queue;
|
|
37
|
+
private readonly logger;
|
|
38
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<EmailBlastDelivery, {
|
|
39
|
+
blast: {
|
|
40
|
+
model: import("../../..").DataMeta;
|
|
41
|
+
direction: "in";
|
|
42
|
+
relationship: string;
|
|
43
|
+
cardinality: "one";
|
|
44
|
+
required: true;
|
|
45
|
+
dtoKey: string;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
constructor(jsonApiService: JsonApiService, deliveryRepository: EmailBlastDeliveryRepository, clsService: ClsService, blastRepository: EmailBlastRepository, optOutRepository: EmailOptOutRepository, enrollmentRepository: EmailAutomationEnrollmentRepository, emailService: EmailService, responseLanguage: ResponseLanguageService, configService: ConfigService<BaseConfigInterface>, cacheService: CacheService, queue: Queue, logger: AppLoggingService);
|
|
49
|
+
/**
|
|
50
|
+
* Snapshots the recipients (once per blast) and sends every pending delivery in batches.
|
|
51
|
+
* Safe to run again after a crash: claimed deliveries are never claimed twice.
|
|
52
|
+
*/
|
|
53
|
+
processBlast(params: {
|
|
54
|
+
blastId: string;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
sent: number;
|
|
57
|
+
skipped: number;
|
|
58
|
+
failed: number;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Fails every delivery claimed more than EMAIL_BLAST_STUCK_MINUTES ago (never resent),
|
|
62
|
+
* then queues the send job again for every blast still `sending`, so a crashed run ends.
|
|
63
|
+
*/
|
|
64
|
+
recoverStuck(params: {
|
|
65
|
+
now: Date;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Stops every marketing email to the delivery's address: counts the unsubscribe on the
|
|
69
|
+
* blast once, finishes the address's active automation enrolments and writes the shared
|
|
70
|
+
* opt-out. Idempotent. Answers nothing, so a token holder never learns the email or user.
|
|
71
|
+
*/
|
|
72
|
+
unsubscribe(params: {
|
|
73
|
+
token: string;
|
|
74
|
+
source: "link" | "header";
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
private deliverOne;
|
|
77
|
+
private invalidateCache;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=email-blast-delivery.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-blast-delivery.service.d.ts","sourceRoot":"","sources":["../../../../src/foundations/email-blast/services/email-blast-delivery.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAA0C,MAAM,4BAA4B,CAAC;AAEzG,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,gDAAgD,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AACjF,OAAO,EAAE,mCAAmC,EAAE,MAAM,4EAA4E,CAAC;AACjI,OAAO,EAAE,qBAAqB,EAAE,MAAM,8DAA8D,CAAC;AACrG,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAUxF,OAAO,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAEpG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAO9E;;;;;;;;;;GAUG;AACH,qBACa,yBAA0B,SAAQ,eAAe,CAC5D,kBAAkB,EAClB,OAAO,4BAA4B,CAAC,aAAa,CAClD;IAKG,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAEnC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IACK,OAAO,CAAC,QAAQ,CAAC,KAAK;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdzB,SAAS,CAAC,QAAQ,CAAC,UAAU;;;;;;;;;OAAgC;gBAG3D,cAAc,EAAE,cAAc,EACb,kBAAkB,EAAE,4BAA4B,EACjE,UAAU,EAAE,UAAU,EACL,eAAe,EAAE,oBAAoB,EACrC,gBAAgB,EAAE,qBAAqB,EACvC,oBAAoB,EAAE,mCAAmC,EACzD,YAAY,EAAE,YAAY,EAC1B,gBAAgB,EAAE,uBAAuB,EACzC,aAAa,EAAE,aAAa,CAAC,mBAAmB,CAAC,EACjD,YAAY,EAAE,YAAY,EACQ,KAAK,EAAE,KAAK,EAC9C,MAAM,EAAE,iBAAiB;IAK5C;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IA2C3G;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxD;;;;OAIG;IACG,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAqBxE,UAAU;YAoDV,eAAe;CAG9B"}
|