@metigan/angular 1.0.0
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/LICENSE +22 -0
- package/README.md +581 -0
- package/dist/LICENSE +22 -0
- package/dist/README.md +581 -0
- package/dist/esm2022/metigan-angular.mjs +5 -0
- package/dist/esm2022/public-api.mjs +21 -0
- package/dist/esm2022/src/lib/audiences.service.mjs +157 -0
- package/dist/esm2022/src/lib/config.mjs +30 -0
- package/dist/esm2022/src/lib/contacts.service.mjs +267 -0
- package/dist/esm2022/src/lib/email.service.mjs +267 -0
- package/dist/esm2022/src/lib/errors.mjs +40 -0
- package/dist/esm2022/src/lib/forms.service.mjs +180 -0
- package/dist/esm2022/src/lib/http-client.service.mjs +111 -0
- package/dist/esm2022/src/lib/metigan.module.mjs +67 -0
- package/dist/esm2022/src/lib/metigan.service.mjs +72 -0
- package/dist/esm2022/src/lib/templates.service.mjs +85 -0
- package/dist/esm2022/src/lib/types.mjs +6 -0
- package/dist/fesm2022/metigan-angular.mjs +1241 -0
- package/dist/fesm2022/metigan-angular.mjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/public-api.d.ts +15 -0
- package/dist/src/lib/audiences.service.d.ts +62 -0
- package/dist/src/lib/config.d.ts +28 -0
- package/dist/src/lib/contacts.service.d.ts +80 -0
- package/dist/src/lib/email.service.d.ts +44 -0
- package/dist/src/lib/errors.d.ts +24 -0
- package/dist/src/lib/forms.service.d.ts +67 -0
- package/dist/src/lib/http-client.service.d.ts +46 -0
- package/dist/src/lib/metigan.module.d.ts +27 -0
- package/dist/src/lib/metigan.service.d.ts +27 -0
- package/dist/src/lib/templates.service.d.ts +36 -0
- package/dist/src/lib/types.d.ts +329 -0
- package/examples/basic.component.ts +113 -0
- package/ng-package.json +8 -0
- package/package.json +68 -0
- package/public-api.ts +26 -0
- package/src/lib/audiences.service.ts +230 -0
- package/src/lib/config.ts +35 -0
- package/src/lib/contacts.service.ts +377 -0
- package/src/lib/email.service.ts +286 -0
- package/src/lib/errors.ts +45 -0
- package/src/lib/forms.service.ts +263 -0
- package/src/lib/http-client.service.ts +156 -0
- package/src/lib/metigan.module.ts +55 -0
- package/src/lib/metigan.service.ts +80 -0
- package/src/lib/templates.service.ts +103 -0
- package/src/lib/types.ts +398 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, Optional, Inject, NgModule } from '@angular/core';
|
|
3
|
+
import { timer, throwError, Observable } from 'rxjs';
|
|
4
|
+
import { map, retryWhen, mergeMap, catchError } from 'rxjs/operators';
|
|
5
|
+
import * as i1 from '@angular/common/http';
|
|
6
|
+
import { HttpParams, HttpClientModule } from '@angular/common/http';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom error classes for Metigan SDK
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Base error class for all Metigan errors
|
|
13
|
+
*/
|
|
14
|
+
class MetiganError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'MetiganError';
|
|
18
|
+
Object.setPrototypeOf(this, MetiganError.prototype);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validation error - thrown when input validation fails
|
|
23
|
+
*/
|
|
24
|
+
class ValidationError extends MetiganError {
|
|
25
|
+
field;
|
|
26
|
+
constructor(message, field) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'ValidationError';
|
|
29
|
+
this.field = field;
|
|
30
|
+
Object.setPrototypeOf(this, ValidationError.prototype);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* API error - thrown when API request fails
|
|
35
|
+
*/
|
|
36
|
+
class ApiError extends MetiganError {
|
|
37
|
+
statusCode;
|
|
38
|
+
status;
|
|
39
|
+
constructor(message, statusCode) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = 'ApiError';
|
|
42
|
+
this.statusCode = statusCode || statusCode;
|
|
43
|
+
this.status = statusCode;
|
|
44
|
+
Object.setPrototypeOf(this, ApiError.prototype);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Metigan Library Configuration
|
|
50
|
+
* Central configuration file for all modules
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* Default API URL for Metigan services
|
|
54
|
+
* All API calls are routed through this single endpoint
|
|
55
|
+
*
|
|
56
|
+
* Can be overridden with METIGAN_API_URL environment variable for testing
|
|
57
|
+
*/
|
|
58
|
+
const API_URL = (typeof process !== 'undefined' && process.env?.['METIGAN_API_URL'])
|
|
59
|
+
|| 'https://api.metigan.com';
|
|
60
|
+
/**
|
|
61
|
+
* SDK Version
|
|
62
|
+
*/
|
|
63
|
+
const SDK_VERSION = '1.0.0';
|
|
64
|
+
/**
|
|
65
|
+
* Default timeout for API requests (in milliseconds)
|
|
66
|
+
*/
|
|
67
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
68
|
+
/**
|
|
69
|
+
* Default retry configuration
|
|
70
|
+
*/
|
|
71
|
+
const DEFAULT_RETRY_COUNT = 3;
|
|
72
|
+
const DEFAULT_RETRY_DELAY = 1000;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum file size for attachments (7MB)
|
|
75
|
+
*/
|
|
76
|
+
const MAX_FILE_SIZE = 7 * 1024 * 1024;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* HTTP Client Service for Metigan SDK
|
|
80
|
+
* Wraps Angular HttpClient with retry logic and error handling
|
|
81
|
+
*/
|
|
82
|
+
class MetiganHttpClient {
|
|
83
|
+
http;
|
|
84
|
+
constructor(http) {
|
|
85
|
+
this.http = http;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Make GET request with retry logic
|
|
89
|
+
*/
|
|
90
|
+
get(url, options, retryCount = 3, retryDelay = 1000) {
|
|
91
|
+
const opts = this.buildOptions(options);
|
|
92
|
+
return this.http.get(url, opts).pipe(map((response) => response), retryWhen(errors => errors.pipe(mergeMap((error, index) => {
|
|
93
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
94
|
+
return timer(retryDelay * (index + 1));
|
|
95
|
+
}
|
|
96
|
+
return throwError(() => error);
|
|
97
|
+
}))), catchError(this.handleError));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Make POST request with retry logic
|
|
101
|
+
*/
|
|
102
|
+
post(url, body, options, retryCount = 3, retryDelay = 1000) {
|
|
103
|
+
const opts = this.buildOptions(options);
|
|
104
|
+
return this.http.post(url, body, opts).pipe(map((response) => response), retryWhen(errors => errors.pipe(mergeMap((error, index) => {
|
|
105
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
106
|
+
return timer(retryDelay * (index + 1));
|
|
107
|
+
}
|
|
108
|
+
return throwError(() => error);
|
|
109
|
+
}))), catchError(this.handleError));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Make PUT request with retry logic
|
|
113
|
+
*/
|
|
114
|
+
put(url, body, options, retryCount = 3, retryDelay = 1000) {
|
|
115
|
+
const opts = this.buildOptions(options);
|
|
116
|
+
return this.http.put(url, body, opts).pipe(map((response) => response), retryWhen(errors => errors.pipe(mergeMap((error, index) => {
|
|
117
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
118
|
+
return timer(retryDelay * (index + 1));
|
|
119
|
+
}
|
|
120
|
+
return throwError(() => error);
|
|
121
|
+
}))), catchError(this.handleError));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Make DELETE request with retry logic
|
|
125
|
+
*/
|
|
126
|
+
delete(url, options, retryCount = 3, retryDelay = 1000) {
|
|
127
|
+
const opts = this.buildOptions(options);
|
|
128
|
+
return this.http.delete(url, opts).pipe(map((response) => response), retryWhen(errors => errors.pipe(mergeMap((error, index) => {
|
|
129
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
130
|
+
return timer(retryDelay * (index + 1));
|
|
131
|
+
}
|
|
132
|
+
return throwError(() => error);
|
|
133
|
+
}))), catchError(this.handleError));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build HTTP options with timeout
|
|
137
|
+
*/
|
|
138
|
+
buildOptions(options) {
|
|
139
|
+
const opts = {
|
|
140
|
+
observe: 'body'
|
|
141
|
+
};
|
|
142
|
+
if (options?.headers) {
|
|
143
|
+
opts.headers = options.headers;
|
|
144
|
+
}
|
|
145
|
+
if (options?.params) {
|
|
146
|
+
opts.params = options.params;
|
|
147
|
+
}
|
|
148
|
+
// Angular HttpClient doesn't support timeout directly
|
|
149
|
+
// Timeout should be handled at the interceptor level if needed
|
|
150
|
+
return opts;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Determine if error should trigger retry
|
|
154
|
+
*/
|
|
155
|
+
shouldRetry(error) {
|
|
156
|
+
// Retry on server errors (5xx) or network errors
|
|
157
|
+
return error.status >= 500 || error.status === 0 || !error.status;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Handle HTTP errors
|
|
161
|
+
*/
|
|
162
|
+
handleError = (error) => {
|
|
163
|
+
if (error.error instanceof ErrorEvent) {
|
|
164
|
+
// Client-side error
|
|
165
|
+
return throwError(() => new MetiganError(`Network error: ${error.error.message}`));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Server-side error
|
|
169
|
+
const message = error.error?.message || error.error?.error || `Request failed with status ${error.status}`;
|
|
170
|
+
return throwError(() => new ApiError(message, error.status));
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganHttpClient, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
174
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganHttpClient, providedIn: 'root' });
|
|
175
|
+
}
|
|
176
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganHttpClient, decorators: [{
|
|
177
|
+
type: Injectable,
|
|
178
|
+
args: [{
|
|
179
|
+
providedIn: 'root'
|
|
180
|
+
}]
|
|
181
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Metigan Email Service
|
|
185
|
+
* Service for sending emails through the Metigan API
|
|
186
|
+
*/
|
|
187
|
+
class MetiganEmailService {
|
|
188
|
+
http;
|
|
189
|
+
apiKey = '';
|
|
190
|
+
apiUrl = API_URL;
|
|
191
|
+
timeout = DEFAULT_TIMEOUT;
|
|
192
|
+
retryCount = DEFAULT_RETRY_COUNT;
|
|
193
|
+
retryDelay = DEFAULT_RETRY_DELAY;
|
|
194
|
+
constructor(http) {
|
|
195
|
+
this.http = http;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Initialize the service with API key and options
|
|
199
|
+
*/
|
|
200
|
+
initialize(apiKey, options) {
|
|
201
|
+
if (!apiKey) {
|
|
202
|
+
throw new MetiganError('API key is required');
|
|
203
|
+
}
|
|
204
|
+
this.apiKey = apiKey;
|
|
205
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
206
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
207
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
208
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Validate email address format
|
|
212
|
+
*/
|
|
213
|
+
validateEmail(email) {
|
|
214
|
+
if (!email || typeof email !== 'string')
|
|
215
|
+
return false;
|
|
216
|
+
const parts = email.split('@');
|
|
217
|
+
if (parts.length !== 2)
|
|
218
|
+
return false;
|
|
219
|
+
if (parts[0].length === 0)
|
|
220
|
+
return false;
|
|
221
|
+
const domainParts = parts[1].split('.');
|
|
222
|
+
if (domainParts.length < 2)
|
|
223
|
+
return false;
|
|
224
|
+
if (domainParts.some(part => part.length === 0))
|
|
225
|
+
return false;
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Extract email address from "Name <email>" format
|
|
230
|
+
*/
|
|
231
|
+
extractEmailAddress(from) {
|
|
232
|
+
if (!from)
|
|
233
|
+
return '';
|
|
234
|
+
const angleMatch = from.match(/<([^>]+)>/);
|
|
235
|
+
if (angleMatch) {
|
|
236
|
+
return angleMatch[1].trim();
|
|
237
|
+
}
|
|
238
|
+
return from.trim();
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Validate email options
|
|
242
|
+
*/
|
|
243
|
+
validateOptions(options) {
|
|
244
|
+
if (!options.from) {
|
|
245
|
+
throw new ValidationError('Sender email (from) is required');
|
|
246
|
+
}
|
|
247
|
+
if (!options.recipients || !Array.isArray(options.recipients) || options.recipients.length === 0) {
|
|
248
|
+
throw new ValidationError('Recipients must be a non-empty array');
|
|
249
|
+
}
|
|
250
|
+
if (!options.subject) {
|
|
251
|
+
throw new ValidationError('Subject is required');
|
|
252
|
+
}
|
|
253
|
+
if (!options.content && !options.templateId) {
|
|
254
|
+
throw new ValidationError('Either content or templateId is required');
|
|
255
|
+
}
|
|
256
|
+
const fromEmail = this.extractEmailAddress(options.from);
|
|
257
|
+
if (!this.validateEmail(fromEmail)) {
|
|
258
|
+
throw new ValidationError(`Invalid sender email format: ${fromEmail}`);
|
|
259
|
+
}
|
|
260
|
+
for (const recipient of options.recipients) {
|
|
261
|
+
const recipientEmail = this.extractEmailAddress(recipient);
|
|
262
|
+
if (!this.validateEmail(recipientEmail)) {
|
|
263
|
+
throw new ValidationError(`Invalid recipient email format: ${recipientEmail}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (options.cc) {
|
|
267
|
+
for (const cc of options.cc) {
|
|
268
|
+
const ccEmail = this.extractEmailAddress(cc);
|
|
269
|
+
if (!this.validateEmail(ccEmail)) {
|
|
270
|
+
throw new ValidationError(`Invalid CC email format: ${ccEmail}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (options.bcc) {
|
|
275
|
+
for (const bcc of options.bcc) {
|
|
276
|
+
const bccEmail = this.extractEmailAddress(bcc);
|
|
277
|
+
if (!this.validateEmail(bccEmail)) {
|
|
278
|
+
throw new ValidationError(`Invalid BCC email format: ${bccEmail}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (options.replyTo) {
|
|
283
|
+
const replyToEmail = this.extractEmailAddress(options.replyTo);
|
|
284
|
+
if (!this.validateEmail(replyToEmail)) {
|
|
285
|
+
throw new ValidationError(`Invalid reply-to email format: ${replyToEmail}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Process attachments for FormData
|
|
291
|
+
*/
|
|
292
|
+
async processAttachments(attachments) {
|
|
293
|
+
const formData = new FormData();
|
|
294
|
+
for (const file of attachments) {
|
|
295
|
+
if (file instanceof File) {
|
|
296
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
297
|
+
throw new MetiganError(`File ${file.name} exceeds the maximum size of 7MB`);
|
|
298
|
+
}
|
|
299
|
+
formData.append('files', file);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
const attachment = file;
|
|
303
|
+
// Convert content to Blob
|
|
304
|
+
let blob;
|
|
305
|
+
if (attachment.content instanceof ArrayBuffer) {
|
|
306
|
+
blob = new Blob([attachment.content], { type: attachment.contentType });
|
|
307
|
+
}
|
|
308
|
+
else if (attachment.content instanceof Uint8Array) {
|
|
309
|
+
blob = new Blob([attachment.content], { type: attachment.contentType });
|
|
310
|
+
}
|
|
311
|
+
else if (typeof attachment.content === 'string') {
|
|
312
|
+
// Base64 string
|
|
313
|
+
const byteCharacters = atob(attachment.content);
|
|
314
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
315
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
316
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
317
|
+
}
|
|
318
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
319
|
+
blob = new Blob([byteArray], { type: attachment.contentType });
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
throw new MetiganError('Invalid attachment content type');
|
|
323
|
+
}
|
|
324
|
+
if (blob.size > MAX_FILE_SIZE) {
|
|
325
|
+
throw new MetiganError(`File ${attachment.filename} exceeds the maximum size of 7MB`);
|
|
326
|
+
}
|
|
327
|
+
const fileObj = new File([blob], attachment.filename, { type: attachment.contentType });
|
|
328
|
+
formData.append('files', fileObj);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return formData;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Send an email
|
|
335
|
+
*/
|
|
336
|
+
sendEmail(options) {
|
|
337
|
+
if (!this.apiKey) {
|
|
338
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
this.validateOptions(options);
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
return throwError(() => error);
|
|
345
|
+
}
|
|
346
|
+
const headers = {
|
|
347
|
+
'x-api-key': this.apiKey,
|
|
348
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
349
|
+
};
|
|
350
|
+
// If attachments, use FormData
|
|
351
|
+
if (options.attachments && options.attachments.length > 0) {
|
|
352
|
+
return new Observable(observer => {
|
|
353
|
+
this.processAttachments(options.attachments).then(formData => {
|
|
354
|
+
formData.append('from', options.from);
|
|
355
|
+
formData.append('recipients', JSON.stringify(options.recipients));
|
|
356
|
+
formData.append('subject', options.subject);
|
|
357
|
+
if (options.templateId) {
|
|
358
|
+
formData.append('useTemplate', 'true');
|
|
359
|
+
formData.append('templateId', options.templateId);
|
|
360
|
+
}
|
|
361
|
+
else if (options.content) {
|
|
362
|
+
formData.append('content', options.content);
|
|
363
|
+
}
|
|
364
|
+
if (options.cc && options.cc.length > 0) {
|
|
365
|
+
formData.append('cc', JSON.stringify(options.cc));
|
|
366
|
+
}
|
|
367
|
+
if (options.bcc && options.bcc.length > 0) {
|
|
368
|
+
formData.append('bcc', JSON.stringify(options.bcc));
|
|
369
|
+
}
|
|
370
|
+
if (options.replyTo) {
|
|
371
|
+
formData.append('replyTo', options.replyTo);
|
|
372
|
+
}
|
|
373
|
+
const httpOptions = {
|
|
374
|
+
headers: headers
|
|
375
|
+
};
|
|
376
|
+
this.http.post(`${this.apiUrl}/api/email/send`, formData, httpOptions, this.retryCount, this.retryDelay)
|
|
377
|
+
.pipe(map(response => {
|
|
378
|
+
if ('success' in response && response.success) {
|
|
379
|
+
return response;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
throw new MetiganError(response.message || 'Failed to send email');
|
|
383
|
+
}
|
|
384
|
+
}))
|
|
385
|
+
.subscribe({
|
|
386
|
+
next: (response) => observer.next(response),
|
|
387
|
+
error: (error) => observer.error(error),
|
|
388
|
+
complete: () => observer.complete()
|
|
389
|
+
});
|
|
390
|
+
}).catch(error => observer.error(error));
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// No attachments, use JSON
|
|
395
|
+
const body = {
|
|
396
|
+
from: options.from,
|
|
397
|
+
recipients: options.recipients,
|
|
398
|
+
subject: options.subject
|
|
399
|
+
};
|
|
400
|
+
if (options.templateId) {
|
|
401
|
+
body.useTemplate = 'true';
|
|
402
|
+
body.templateId = options.templateId;
|
|
403
|
+
}
|
|
404
|
+
else if (options.content) {
|
|
405
|
+
body.content = options.content;
|
|
406
|
+
}
|
|
407
|
+
if (options.cc && options.cc.length > 0) {
|
|
408
|
+
body.cc = options.cc;
|
|
409
|
+
}
|
|
410
|
+
if (options.bcc && options.bcc.length > 0) {
|
|
411
|
+
body.bcc = options.bcc;
|
|
412
|
+
}
|
|
413
|
+
if (options.replyTo) {
|
|
414
|
+
body.replyTo = options.replyTo;
|
|
415
|
+
}
|
|
416
|
+
const httpOptions = {
|
|
417
|
+
headers: {
|
|
418
|
+
...headers,
|
|
419
|
+
'Content-Type': 'application/json'
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
return this.http.post(`${this.apiUrl}/api/email/send`, body, httpOptions, this.retryCount, this.retryDelay)
|
|
423
|
+
.pipe(map(response => {
|
|
424
|
+
if ('success' in response && response.success) {
|
|
425
|
+
return response;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
throw new MetiganError(response.message || 'Failed to send email');
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganEmailService, deps: [{ token: MetiganHttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
434
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganEmailService, providedIn: 'root' });
|
|
435
|
+
}
|
|
436
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganEmailService, decorators: [{
|
|
437
|
+
type: Injectable,
|
|
438
|
+
args: [{
|
|
439
|
+
providedIn: 'root'
|
|
440
|
+
}]
|
|
441
|
+
}], ctorParameters: () => [{ type: MetiganHttpClient }] });
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Metigan Forms Service
|
|
445
|
+
* Service for form management and submissions
|
|
446
|
+
*/
|
|
447
|
+
class MetiganFormsService {
|
|
448
|
+
http;
|
|
449
|
+
apiKey = '';
|
|
450
|
+
apiUrl = API_URL;
|
|
451
|
+
timeout = DEFAULT_TIMEOUT;
|
|
452
|
+
retryCount = DEFAULT_RETRY_COUNT;
|
|
453
|
+
retryDelay = DEFAULT_RETRY_DELAY;
|
|
454
|
+
constructor(http) {
|
|
455
|
+
this.http = http;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Initialize the service with API key and options
|
|
459
|
+
*/
|
|
460
|
+
initialize(apiKey, options) {
|
|
461
|
+
if (!apiKey) {
|
|
462
|
+
throw new MetiganError('API key is required');
|
|
463
|
+
}
|
|
464
|
+
this.apiKey = apiKey;
|
|
465
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
466
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
467
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
468
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Get default headers
|
|
472
|
+
*/
|
|
473
|
+
getHeaders() {
|
|
474
|
+
return {
|
|
475
|
+
'Content-Type': 'application/json',
|
|
476
|
+
'x-api-key': this.apiKey,
|
|
477
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Submit form data
|
|
482
|
+
*/
|
|
483
|
+
submit(options) {
|
|
484
|
+
if (!this.apiKey) {
|
|
485
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
486
|
+
}
|
|
487
|
+
if (!options.formId) {
|
|
488
|
+
return throwError(() => new ValidationError('Form ID is required'));
|
|
489
|
+
}
|
|
490
|
+
if (!options.data || Object.keys(options.data).length === 0) {
|
|
491
|
+
return throwError(() => new ValidationError('Submission data is required'));
|
|
492
|
+
}
|
|
493
|
+
const body = {
|
|
494
|
+
formId: options.formId,
|
|
495
|
+
data: options.data
|
|
496
|
+
};
|
|
497
|
+
return this.http.post(`${this.apiUrl}/api/submissions`, body, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Get form by ID or slug
|
|
501
|
+
*/
|
|
502
|
+
getForm(formIdOrSlug) {
|
|
503
|
+
if (!this.apiKey) {
|
|
504
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
505
|
+
}
|
|
506
|
+
if (!formIdOrSlug) {
|
|
507
|
+
return throwError(() => new ValidationError('Form ID or slug is required'));
|
|
508
|
+
}
|
|
509
|
+
return this.http.get(`${this.apiUrl}/api/forms/${formIdOrSlug}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Get public form by slug
|
|
513
|
+
*/
|
|
514
|
+
getPublicForm(slug) {
|
|
515
|
+
if (!slug) {
|
|
516
|
+
return throwError(() => new ValidationError('Form slug is required'));
|
|
517
|
+
}
|
|
518
|
+
// Public forms don't require API key
|
|
519
|
+
return this.http.get(`${this.apiUrl}/f/${slug}/api`, {}, this.retryCount, this.retryDelay);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* List all forms
|
|
523
|
+
*/
|
|
524
|
+
listForms(options) {
|
|
525
|
+
if (!this.apiKey) {
|
|
526
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
527
|
+
}
|
|
528
|
+
let params = new HttpParams();
|
|
529
|
+
if (options?.page) {
|
|
530
|
+
params = params.set('page', options.page.toString());
|
|
531
|
+
}
|
|
532
|
+
if (options?.limit) {
|
|
533
|
+
params = params.set('limit', options.limit.toString());
|
|
534
|
+
}
|
|
535
|
+
return this.http.get(`${this.apiUrl}/api/forms`, {
|
|
536
|
+
headers: this.getHeaders(),
|
|
537
|
+
params: params
|
|
538
|
+
}, this.retryCount, this.retryDelay);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Create a new form
|
|
542
|
+
*/
|
|
543
|
+
createForm(config) {
|
|
544
|
+
if (!this.apiKey) {
|
|
545
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
546
|
+
}
|
|
547
|
+
if (!config.title) {
|
|
548
|
+
return throwError(() => new ValidationError('Form title is required'));
|
|
549
|
+
}
|
|
550
|
+
if (!config.fields || config.fields.length === 0) {
|
|
551
|
+
return throwError(() => new ValidationError('At least one field is required'));
|
|
552
|
+
}
|
|
553
|
+
return this.http.post(`${this.apiUrl}/api/forms`, config, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Update an existing form
|
|
557
|
+
*/
|
|
558
|
+
updateForm(formId, config) {
|
|
559
|
+
if (!this.apiKey) {
|
|
560
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
561
|
+
}
|
|
562
|
+
if (!formId) {
|
|
563
|
+
return throwError(() => new ValidationError('Form ID is required'));
|
|
564
|
+
}
|
|
565
|
+
return this.http.put(`${this.apiUrl}/api/forms/${formId}`, config, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Delete a form
|
|
569
|
+
*/
|
|
570
|
+
deleteForm(formId) {
|
|
571
|
+
if (!this.apiKey) {
|
|
572
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
573
|
+
}
|
|
574
|
+
if (!formId) {
|
|
575
|
+
return throwError(() => new ValidationError('Form ID is required'));
|
|
576
|
+
}
|
|
577
|
+
return this.http.delete(`${this.apiUrl}/api/forms/${formId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get form analytics
|
|
581
|
+
*/
|
|
582
|
+
getAnalytics(formId) {
|
|
583
|
+
if (!this.apiKey) {
|
|
584
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
585
|
+
}
|
|
586
|
+
if (!formId) {
|
|
587
|
+
return throwError(() => new ValidationError('Form ID is required'));
|
|
588
|
+
}
|
|
589
|
+
return this.http.get(`${this.apiUrl}/api/forms/${formId}/analytics`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Publish a form
|
|
593
|
+
*/
|
|
594
|
+
publishForm(formId, slug) {
|
|
595
|
+
if (!this.apiKey) {
|
|
596
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
597
|
+
}
|
|
598
|
+
if (!formId) {
|
|
599
|
+
return throwError(() => new ValidationError('Form ID is required'));
|
|
600
|
+
}
|
|
601
|
+
if (!slug) {
|
|
602
|
+
return throwError(() => new ValidationError('Slug is required'));
|
|
603
|
+
}
|
|
604
|
+
return this.http.post(`${this.apiUrl}/api/forms/${formId}/publish`, { slug }, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
605
|
+
}
|
|
606
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganFormsService, deps: [{ token: MetiganHttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
607
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganFormsService, providedIn: 'root' });
|
|
608
|
+
}
|
|
609
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganFormsService, decorators: [{
|
|
610
|
+
type: Injectable,
|
|
611
|
+
args: [{
|
|
612
|
+
providedIn: 'root'
|
|
613
|
+
}]
|
|
614
|
+
}], ctorParameters: () => [{ type: MetiganHttpClient }] });
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Metigan Contacts Service
|
|
618
|
+
* Service for contact/subscriber management
|
|
619
|
+
*/
|
|
620
|
+
class MetiganContactsService {
|
|
621
|
+
http;
|
|
622
|
+
apiKey = '';
|
|
623
|
+
apiUrl = API_URL;
|
|
624
|
+
timeout = DEFAULT_TIMEOUT;
|
|
625
|
+
retryCount = DEFAULT_RETRY_COUNT;
|
|
626
|
+
retryDelay = DEFAULT_RETRY_DELAY;
|
|
627
|
+
constructor(http) {
|
|
628
|
+
this.http = http;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Initialize the service with API key and options
|
|
632
|
+
*/
|
|
633
|
+
initialize(apiKey, options) {
|
|
634
|
+
if (!apiKey) {
|
|
635
|
+
throw new MetiganError('API key is required');
|
|
636
|
+
}
|
|
637
|
+
this.apiKey = apiKey;
|
|
638
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
639
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
640
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
641
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Get default headers
|
|
645
|
+
*/
|
|
646
|
+
getHeaders() {
|
|
647
|
+
return {
|
|
648
|
+
'Content-Type': 'application/json',
|
|
649
|
+
'x-api-key': this.apiKey,
|
|
650
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Validate email format
|
|
655
|
+
*/
|
|
656
|
+
validateEmail(email) {
|
|
657
|
+
if (!email || typeof email !== 'string')
|
|
658
|
+
return false;
|
|
659
|
+
const parts = email.split('@');
|
|
660
|
+
if (parts.length !== 2)
|
|
661
|
+
return false;
|
|
662
|
+
if (parts[0].length === 0)
|
|
663
|
+
return false;
|
|
664
|
+
const domainParts = parts[1].split('.');
|
|
665
|
+
if (domainParts.length < 2)
|
|
666
|
+
return false;
|
|
667
|
+
if (domainParts.some(part => part.length === 0))
|
|
668
|
+
return false;
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Create a new contact
|
|
673
|
+
*/
|
|
674
|
+
create(options) {
|
|
675
|
+
if (!this.apiKey) {
|
|
676
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
677
|
+
}
|
|
678
|
+
if (!options.email || !this.validateEmail(options.email)) {
|
|
679
|
+
return throwError(() => new ValidationError('Valid email is required'));
|
|
680
|
+
}
|
|
681
|
+
if (!options.audienceId) {
|
|
682
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
683
|
+
}
|
|
684
|
+
return this.http.post(`${this.apiUrl}/api/contacts`, options, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Get contact by ID
|
|
688
|
+
*/
|
|
689
|
+
get(contactId) {
|
|
690
|
+
if (!this.apiKey) {
|
|
691
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
692
|
+
}
|
|
693
|
+
if (!contactId) {
|
|
694
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
695
|
+
}
|
|
696
|
+
return this.http.get(`${this.apiUrl}/api/contacts/${contactId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Get contact by email
|
|
700
|
+
*/
|
|
701
|
+
getByEmail(email, audienceId) {
|
|
702
|
+
if (!this.apiKey) {
|
|
703
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
704
|
+
}
|
|
705
|
+
if (!email || !this.validateEmail(email)) {
|
|
706
|
+
return throwError(() => new ValidationError('Valid email is required'));
|
|
707
|
+
}
|
|
708
|
+
if (!audienceId) {
|
|
709
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
710
|
+
}
|
|
711
|
+
let params = new HttpParams();
|
|
712
|
+
params = params.set('email', email);
|
|
713
|
+
params = params.set('audienceId', audienceId);
|
|
714
|
+
return this.http.get(`${this.apiUrl}/api/contacts`, {
|
|
715
|
+
headers: this.getHeaders(),
|
|
716
|
+
params: params
|
|
717
|
+
}, this.retryCount, this.retryDelay);
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Update a contact
|
|
721
|
+
*/
|
|
722
|
+
update(contactId, options) {
|
|
723
|
+
if (!this.apiKey) {
|
|
724
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
725
|
+
}
|
|
726
|
+
if (!contactId) {
|
|
727
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
728
|
+
}
|
|
729
|
+
return this.http.put(`${this.apiUrl}/api/contacts/${contactId}`, options, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Delete a contact
|
|
733
|
+
*/
|
|
734
|
+
delete(contactId) {
|
|
735
|
+
if (!this.apiKey) {
|
|
736
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
737
|
+
}
|
|
738
|
+
if (!contactId) {
|
|
739
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
740
|
+
}
|
|
741
|
+
return this.http.delete(`${this.apiUrl}/api/contacts/${contactId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* List contacts with filters
|
|
745
|
+
*/
|
|
746
|
+
list(filters) {
|
|
747
|
+
if (!this.apiKey) {
|
|
748
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
749
|
+
}
|
|
750
|
+
let params = new HttpParams();
|
|
751
|
+
if (filters?.audienceId) {
|
|
752
|
+
params = params.set('audienceId', filters.audienceId);
|
|
753
|
+
}
|
|
754
|
+
if (filters?.status) {
|
|
755
|
+
params = params.set('status', filters.status);
|
|
756
|
+
}
|
|
757
|
+
if (filters?.tag) {
|
|
758
|
+
params = params.set('tag', filters.tag);
|
|
759
|
+
}
|
|
760
|
+
if (filters?.search) {
|
|
761
|
+
params = params.set('search', filters.search);
|
|
762
|
+
}
|
|
763
|
+
if (filters?.page) {
|
|
764
|
+
params = params.set('page', filters.page.toString());
|
|
765
|
+
}
|
|
766
|
+
if (filters?.limit) {
|
|
767
|
+
params = params.set('limit', filters.limit.toString());
|
|
768
|
+
}
|
|
769
|
+
return this.http.get(`${this.apiUrl}/api/contacts`, {
|
|
770
|
+
headers: this.getHeaders(),
|
|
771
|
+
params: params
|
|
772
|
+
}, this.retryCount, this.retryDelay);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Search contacts
|
|
776
|
+
*/
|
|
777
|
+
search(query, audienceId) {
|
|
778
|
+
if (!this.apiKey) {
|
|
779
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
780
|
+
}
|
|
781
|
+
if (!query) {
|
|
782
|
+
return throwError(() => new ValidationError('Search query is required'));
|
|
783
|
+
}
|
|
784
|
+
let params = new HttpParams();
|
|
785
|
+
params = params.set('search', query);
|
|
786
|
+
if (audienceId) {
|
|
787
|
+
params = params.set('audienceId', audienceId);
|
|
788
|
+
}
|
|
789
|
+
return this.http.get(`${this.apiUrl}/api/contacts/search`, {
|
|
790
|
+
headers: this.getHeaders(),
|
|
791
|
+
params: params
|
|
792
|
+
}, this.retryCount, this.retryDelay);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Bulk import contacts
|
|
796
|
+
*/
|
|
797
|
+
bulkImport(contacts, audienceId) {
|
|
798
|
+
if (!this.apiKey) {
|
|
799
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
800
|
+
}
|
|
801
|
+
if (!contacts || contacts.length === 0) {
|
|
802
|
+
return throwError(() => new ValidationError('Contacts array is required'));
|
|
803
|
+
}
|
|
804
|
+
if (!audienceId) {
|
|
805
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
806
|
+
}
|
|
807
|
+
return this.http.post(`${this.apiUrl}/api/contacts/bulk`, { contacts, audienceId }, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Subscribe a contact
|
|
811
|
+
*/
|
|
812
|
+
subscribe(contactId) {
|
|
813
|
+
if (!this.apiKey) {
|
|
814
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
815
|
+
}
|
|
816
|
+
if (!contactId) {
|
|
817
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
818
|
+
}
|
|
819
|
+
return this.http.post(`${this.apiUrl}/api/contacts/${contactId}/subscribe`, {}, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Unsubscribe a contact
|
|
823
|
+
*/
|
|
824
|
+
unsubscribe(contactId) {
|
|
825
|
+
if (!this.apiKey) {
|
|
826
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
827
|
+
}
|
|
828
|
+
if (!contactId) {
|
|
829
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
830
|
+
}
|
|
831
|
+
return this.http.post(`${this.apiUrl}/api/contacts/${contactId}/unsubscribe`, {}, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Add tags to a contact
|
|
835
|
+
*/
|
|
836
|
+
addTags(contactId, tags) {
|
|
837
|
+
if (!this.apiKey) {
|
|
838
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
839
|
+
}
|
|
840
|
+
if (!contactId) {
|
|
841
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
842
|
+
}
|
|
843
|
+
if (!tags || tags.length === 0) {
|
|
844
|
+
return throwError(() => new ValidationError('Tags array is required'));
|
|
845
|
+
}
|
|
846
|
+
return this.http.post(`${this.apiUrl}/api/contacts/${contactId}/tags`, { tags }, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Remove tags from a contact
|
|
850
|
+
*/
|
|
851
|
+
removeTags(contactId, tags) {
|
|
852
|
+
if (!this.apiKey) {
|
|
853
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
854
|
+
}
|
|
855
|
+
if (!contactId) {
|
|
856
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
857
|
+
}
|
|
858
|
+
if (!tags || tags.length === 0) {
|
|
859
|
+
return throwError(() => new ValidationError('Tags array is required'));
|
|
860
|
+
}
|
|
861
|
+
return this.http.delete(`${this.apiUrl}/api/contacts/${contactId}/tags`, {
|
|
862
|
+
headers: this.getHeaders(),
|
|
863
|
+
params: { tags: tags.join(',') }
|
|
864
|
+
}, this.retryCount, this.retryDelay);
|
|
865
|
+
}
|
|
866
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganContactsService, deps: [{ token: MetiganHttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
867
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganContactsService, providedIn: 'root' });
|
|
868
|
+
}
|
|
869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganContactsService, decorators: [{
|
|
870
|
+
type: Injectable,
|
|
871
|
+
args: [{
|
|
872
|
+
providedIn: 'root'
|
|
873
|
+
}]
|
|
874
|
+
}], ctorParameters: () => [{ type: MetiganHttpClient }] });
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Metigan Audiences Service
|
|
878
|
+
* Service for audience/list management
|
|
879
|
+
*/
|
|
880
|
+
class MetiganAudiencesService {
|
|
881
|
+
http;
|
|
882
|
+
apiKey = '';
|
|
883
|
+
apiUrl = API_URL;
|
|
884
|
+
timeout = DEFAULT_TIMEOUT;
|
|
885
|
+
retryCount = DEFAULT_RETRY_COUNT;
|
|
886
|
+
retryDelay = DEFAULT_RETRY_DELAY;
|
|
887
|
+
constructor(http) {
|
|
888
|
+
this.http = http;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Initialize the service with API key and options
|
|
892
|
+
*/
|
|
893
|
+
initialize(apiKey, options) {
|
|
894
|
+
if (!apiKey) {
|
|
895
|
+
throw new MetiganError('API key is required');
|
|
896
|
+
}
|
|
897
|
+
this.apiKey = apiKey;
|
|
898
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
899
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
900
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
901
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Get default headers
|
|
905
|
+
*/
|
|
906
|
+
getHeaders() {
|
|
907
|
+
return {
|
|
908
|
+
'Content-Type': 'application/json',
|
|
909
|
+
'x-api-key': this.apiKey,
|
|
910
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Create a new audience
|
|
915
|
+
*/
|
|
916
|
+
create(options) {
|
|
917
|
+
if (!this.apiKey) {
|
|
918
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
919
|
+
}
|
|
920
|
+
if (!options.name) {
|
|
921
|
+
return throwError(() => new ValidationError('Audience name is required'));
|
|
922
|
+
}
|
|
923
|
+
return this.http.post(`${this.apiUrl}/api/audiences`, options, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Get audience by ID
|
|
927
|
+
*/
|
|
928
|
+
get(audienceId) {
|
|
929
|
+
if (!this.apiKey) {
|
|
930
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
931
|
+
}
|
|
932
|
+
if (!audienceId) {
|
|
933
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
934
|
+
}
|
|
935
|
+
return this.http.get(`${this.apiUrl}/api/audiences/${audienceId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Update an audience
|
|
939
|
+
*/
|
|
940
|
+
update(audienceId, options) {
|
|
941
|
+
if (!this.apiKey) {
|
|
942
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
943
|
+
}
|
|
944
|
+
if (!audienceId) {
|
|
945
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
946
|
+
}
|
|
947
|
+
return this.http.put(`${this.apiUrl}/api/audiences/${audienceId}`, options, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Delete an audience
|
|
951
|
+
*/
|
|
952
|
+
delete(audienceId) {
|
|
953
|
+
if (!this.apiKey) {
|
|
954
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
955
|
+
}
|
|
956
|
+
if (!audienceId) {
|
|
957
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
958
|
+
}
|
|
959
|
+
return this.http.delete(`${this.apiUrl}/api/audiences/${audienceId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* List all audiences
|
|
963
|
+
*/
|
|
964
|
+
list(options) {
|
|
965
|
+
if (!this.apiKey) {
|
|
966
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
967
|
+
}
|
|
968
|
+
let params = new HttpParams();
|
|
969
|
+
if (options?.page) {
|
|
970
|
+
params = params.set('page', options.page.toString());
|
|
971
|
+
}
|
|
972
|
+
if (options?.limit) {
|
|
973
|
+
params = params.set('limit', options.limit.toString());
|
|
974
|
+
}
|
|
975
|
+
return this.http.get(`${this.apiUrl}/api/audiences`, {
|
|
976
|
+
headers: this.getHeaders(),
|
|
977
|
+
params: params
|
|
978
|
+
}, this.retryCount, this.retryDelay);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Get audience statistics
|
|
982
|
+
*/
|
|
983
|
+
getStats(audienceId) {
|
|
984
|
+
if (!this.apiKey) {
|
|
985
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
986
|
+
}
|
|
987
|
+
if (!audienceId) {
|
|
988
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
989
|
+
}
|
|
990
|
+
return this.http.get(`${this.apiUrl}/api/audiences/${audienceId}/stats`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Clean audience (remove bounced/unsubscribed contacts)
|
|
994
|
+
*/
|
|
995
|
+
clean(audienceId) {
|
|
996
|
+
if (!this.apiKey) {
|
|
997
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
998
|
+
}
|
|
999
|
+
if (!audienceId) {
|
|
1000
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
1001
|
+
}
|
|
1002
|
+
return this.http.post(`${this.apiUrl}/api/audiences/${audienceId}/clean`, {}, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Merge audiences (source into target)
|
|
1006
|
+
*/
|
|
1007
|
+
merge(sourceAudienceId, targetAudienceId) {
|
|
1008
|
+
if (!this.apiKey) {
|
|
1009
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
1010
|
+
}
|
|
1011
|
+
if (!sourceAudienceId || !targetAudienceId) {
|
|
1012
|
+
return throwError(() => new ValidationError('Source and target audience IDs are required'));
|
|
1013
|
+
}
|
|
1014
|
+
return this.http.post(`${this.apiUrl}/api/audiences/${sourceAudienceId}/merge`, { targetAudienceId }, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
1015
|
+
}
|
|
1016
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganAudiencesService, deps: [{ token: MetiganHttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1017
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganAudiencesService, providedIn: 'root' });
|
|
1018
|
+
}
|
|
1019
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganAudiencesService, decorators: [{
|
|
1020
|
+
type: Injectable,
|
|
1021
|
+
args: [{
|
|
1022
|
+
providedIn: 'root'
|
|
1023
|
+
}]
|
|
1024
|
+
}], ctorParameters: () => [{ type: MetiganHttpClient }] });
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Metigan Templates Service
|
|
1028
|
+
* Service for managing email templates
|
|
1029
|
+
*/
|
|
1030
|
+
class MetiganTemplatesService {
|
|
1031
|
+
http;
|
|
1032
|
+
apiKey = '';
|
|
1033
|
+
apiUrl = API_URL;
|
|
1034
|
+
timeout = DEFAULT_TIMEOUT;
|
|
1035
|
+
retryCount = DEFAULT_RETRY_COUNT;
|
|
1036
|
+
retryDelay = DEFAULT_RETRY_DELAY;
|
|
1037
|
+
constructor(http) {
|
|
1038
|
+
this.http = http;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Initialize the service with API key and options
|
|
1042
|
+
*/
|
|
1043
|
+
initialize(apiKey, options) {
|
|
1044
|
+
if (!apiKey) {
|
|
1045
|
+
throw new MetiganError('API key is required');
|
|
1046
|
+
}
|
|
1047
|
+
this.apiKey = apiKey;
|
|
1048
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
1049
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
1050
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
1051
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Get default headers
|
|
1055
|
+
*/
|
|
1056
|
+
getHeaders() {
|
|
1057
|
+
return {
|
|
1058
|
+
'Content-Type': 'application/json',
|
|
1059
|
+
'x-api-key': this.apiKey,
|
|
1060
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* List all templates
|
|
1065
|
+
*/
|
|
1066
|
+
list(options) {
|
|
1067
|
+
if (!this.apiKey) {
|
|
1068
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
1069
|
+
}
|
|
1070
|
+
let params = new HttpParams();
|
|
1071
|
+
if (options?.page) {
|
|
1072
|
+
params = params.set('page', options.page.toString());
|
|
1073
|
+
}
|
|
1074
|
+
if (options?.limit) {
|
|
1075
|
+
params = params.set('limit', options.limit.toString());
|
|
1076
|
+
}
|
|
1077
|
+
return this.http.get(`${this.apiUrl}/api/templates`, {
|
|
1078
|
+
headers: this.getHeaders(),
|
|
1079
|
+
params: params
|
|
1080
|
+
}, this.retryCount, this.retryDelay);
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Get template by ID
|
|
1084
|
+
*/
|
|
1085
|
+
get(templateId) {
|
|
1086
|
+
if (!this.apiKey) {
|
|
1087
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
1088
|
+
}
|
|
1089
|
+
if (!templateId) {
|
|
1090
|
+
return throwError(() => new ValidationError('Template ID is required'));
|
|
1091
|
+
}
|
|
1092
|
+
return this.http.get(`${this.apiUrl}/api/templates/${templateId}`, { headers: this.getHeaders() }, this.retryCount, this.retryDelay);
|
|
1093
|
+
}
|
|
1094
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganTemplatesService, deps: [{ token: MetiganHttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1095
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganTemplatesService, providedIn: 'root' });
|
|
1096
|
+
}
|
|
1097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganTemplatesService, decorators: [{
|
|
1098
|
+
type: Injectable,
|
|
1099
|
+
args: [{
|
|
1100
|
+
providedIn: 'root'
|
|
1101
|
+
}]
|
|
1102
|
+
}], ctorParameters: () => [{ type: MetiganHttpClient }] });
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Main Metigan Service
|
|
1106
|
+
* Unified service that provides access to all Metigan modules
|
|
1107
|
+
*/
|
|
1108
|
+
const METIGAN_CONFIG = 'METIGAN_CONFIG';
|
|
1109
|
+
class MetiganService {
|
|
1110
|
+
email;
|
|
1111
|
+
forms;
|
|
1112
|
+
contacts;
|
|
1113
|
+
audiences;
|
|
1114
|
+
templates;
|
|
1115
|
+
initialized = false;
|
|
1116
|
+
constructor(emailService, formsService, contactsService, audiencesService, templatesService, config) {
|
|
1117
|
+
this.email = emailService;
|
|
1118
|
+
this.forms = formsService;
|
|
1119
|
+
this.contacts = contactsService;
|
|
1120
|
+
this.audiences = audiencesService;
|
|
1121
|
+
this.templates = templatesService;
|
|
1122
|
+
// Auto-initialize if config is provided via injection
|
|
1123
|
+
if (config) {
|
|
1124
|
+
this.initialize(config);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Initialize all services with API key and options
|
|
1129
|
+
*/
|
|
1130
|
+
initialize(options) {
|
|
1131
|
+
if (!options.apiKey) {
|
|
1132
|
+
throw new MetiganError('API key is required');
|
|
1133
|
+
}
|
|
1134
|
+
const serviceOptions = {
|
|
1135
|
+
apiUrl: options.apiUrl,
|
|
1136
|
+
timeout: options.timeout,
|
|
1137
|
+
retryCount: options.retryCount,
|
|
1138
|
+
retryDelay: options.retryDelay
|
|
1139
|
+
};
|
|
1140
|
+
this.email.initialize(options.apiKey, serviceOptions);
|
|
1141
|
+
this.forms.initialize(options.apiKey, serviceOptions);
|
|
1142
|
+
this.contacts.initialize(options.apiKey, serviceOptions);
|
|
1143
|
+
this.audiences.initialize(options.apiKey, serviceOptions);
|
|
1144
|
+
this.templates.initialize(options.apiKey, serviceOptions);
|
|
1145
|
+
this.initialized = true;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Check if services are initialized
|
|
1149
|
+
*/
|
|
1150
|
+
isInitialized() {
|
|
1151
|
+
return this.initialized;
|
|
1152
|
+
}
|
|
1153
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganService, deps: [{ token: MetiganEmailService }, { token: MetiganFormsService }, { token: MetiganContactsService }, { token: MetiganAudiencesService }, { token: MetiganTemplatesService }, { token: METIGAN_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1154
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganService, providedIn: 'root' });
|
|
1155
|
+
}
|
|
1156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganService, decorators: [{
|
|
1157
|
+
type: Injectable,
|
|
1158
|
+
args: [{
|
|
1159
|
+
providedIn: 'root'
|
|
1160
|
+
}]
|
|
1161
|
+
}], ctorParameters: () => [{ type: MetiganEmailService }, { type: MetiganFormsService }, { type: MetiganContactsService }, { type: MetiganAudiencesService }, { type: MetiganTemplatesService }, { type: undefined, decorators: [{
|
|
1162
|
+
type: Optional
|
|
1163
|
+
}, {
|
|
1164
|
+
type: Inject,
|
|
1165
|
+
args: [METIGAN_CONFIG]
|
|
1166
|
+
}] }] });
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Metigan Angular Module
|
|
1170
|
+
* Import this module in your Angular app to use Metigan services
|
|
1171
|
+
*/
|
|
1172
|
+
class MetiganModule {
|
|
1173
|
+
/**
|
|
1174
|
+
* Configure Metigan with API key and options
|
|
1175
|
+
* Call this in your root module's imports
|
|
1176
|
+
*
|
|
1177
|
+
* @example
|
|
1178
|
+
* ```typescript
|
|
1179
|
+
* imports: [
|
|
1180
|
+
* MetiganModule.forRoot({
|
|
1181
|
+
* apiKey: 'your-api-key'
|
|
1182
|
+
* })
|
|
1183
|
+
* ]
|
|
1184
|
+
* ```
|
|
1185
|
+
*/
|
|
1186
|
+
static forRoot(config) {
|
|
1187
|
+
return {
|
|
1188
|
+
ngModule: MetiganModule,
|
|
1189
|
+
providers: [
|
|
1190
|
+
{
|
|
1191
|
+
provide: METIGAN_CONFIG,
|
|
1192
|
+
useValue: config
|
|
1193
|
+
}
|
|
1194
|
+
]
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1198
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: MetiganModule, imports: [HttpClientModule] });
|
|
1199
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganModule, providers: [
|
|
1200
|
+
MetiganHttpClient,
|
|
1201
|
+
MetiganEmailService,
|
|
1202
|
+
MetiganFormsService,
|
|
1203
|
+
MetiganContactsService,
|
|
1204
|
+
MetiganAudiencesService,
|
|
1205
|
+
MetiganTemplatesService,
|
|
1206
|
+
MetiganService
|
|
1207
|
+
], imports: [HttpClientModule] });
|
|
1208
|
+
}
|
|
1209
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MetiganModule, decorators: [{
|
|
1210
|
+
type: NgModule,
|
|
1211
|
+
args: [{
|
|
1212
|
+
imports: [HttpClientModule],
|
|
1213
|
+
providers: [
|
|
1214
|
+
MetiganHttpClient,
|
|
1215
|
+
MetiganEmailService,
|
|
1216
|
+
MetiganFormsService,
|
|
1217
|
+
MetiganContactsService,
|
|
1218
|
+
MetiganAudiencesService,
|
|
1219
|
+
MetiganTemplatesService,
|
|
1220
|
+
MetiganService
|
|
1221
|
+
]
|
|
1222
|
+
}]
|
|
1223
|
+
}] });
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Type definitions for Metigan Angular SDK
|
|
1227
|
+
* @version 1.0.0
|
|
1228
|
+
*/
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* Public API for @metigan/angular
|
|
1232
|
+
* Export all public classes, services, and types
|
|
1233
|
+
*/
|
|
1234
|
+
// Services
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Generated bundle index. Do not edit.
|
|
1238
|
+
*/
|
|
1239
|
+
|
|
1240
|
+
export { API_URL, ApiError, DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY, DEFAULT_TIMEOUT, MAX_FILE_SIZE, METIGAN_CONFIG, MetiganAudiencesService, MetiganContactsService, MetiganEmailService, MetiganError, MetiganFormsService, MetiganHttpClient, MetiganModule, MetiganService, MetiganTemplatesService, SDK_VERSION, ValidationError };
|
|
1241
|
+
//# sourceMappingURL=metigan-angular.mjs.map
|