@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,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metigan Audiences Service
|
|
3
|
+
* Service for audience/list management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Injectable } from '@angular/core';
|
|
7
|
+
import { Observable, throwError } from 'rxjs';
|
|
8
|
+
import { HttpParams } from '@angular/common/http';
|
|
9
|
+
import { MetiganHttpClient } from './http-client.service';
|
|
10
|
+
import { API_URL, DEFAULT_TIMEOUT, DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY } from './config';
|
|
11
|
+
import { ValidationError, MetiganError } from './errors';
|
|
12
|
+
import {
|
|
13
|
+
Audience,
|
|
14
|
+
CreateAudienceOptions,
|
|
15
|
+
UpdateAudienceOptions,
|
|
16
|
+
AudienceListResponse,
|
|
17
|
+
AudienceStats,
|
|
18
|
+
PaginationOptions
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
@Injectable({
|
|
22
|
+
providedIn: 'root'
|
|
23
|
+
})
|
|
24
|
+
export class MetiganAudiencesService {
|
|
25
|
+
private apiKey: string = '';
|
|
26
|
+
private apiUrl: string = API_URL;
|
|
27
|
+
private timeout: number = DEFAULT_TIMEOUT;
|
|
28
|
+
private retryCount: number = DEFAULT_RETRY_COUNT;
|
|
29
|
+
private retryDelay: number = DEFAULT_RETRY_DELAY;
|
|
30
|
+
|
|
31
|
+
constructor(private http: MetiganHttpClient) {}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the service with API key and options
|
|
35
|
+
*/
|
|
36
|
+
initialize(apiKey: string, options?: { apiUrl?: string; timeout?: number; retryCount?: number; retryDelay?: number }): void {
|
|
37
|
+
if (!apiKey) {
|
|
38
|
+
throw new MetiganError('API key is required');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.apiKey = apiKey;
|
|
42
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
43
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
44
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
45
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get default headers
|
|
50
|
+
*/
|
|
51
|
+
private getHeaders() {
|
|
52
|
+
return {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'x-api-key': this.apiKey,
|
|
55
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a new audience
|
|
61
|
+
*/
|
|
62
|
+
create(options: CreateAudienceOptions): Observable<Audience> {
|
|
63
|
+
if (!this.apiKey) {
|
|
64
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!options.name) {
|
|
68
|
+
return throwError(() => new ValidationError('Audience name is required'));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return this.http.post<Audience>(
|
|
72
|
+
`${this.apiUrl}/api/audiences`,
|
|
73
|
+
options,
|
|
74
|
+
{ headers: this.getHeaders() },
|
|
75
|
+
this.retryCount,
|
|
76
|
+
this.retryDelay
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get audience by ID
|
|
82
|
+
*/
|
|
83
|
+
get(audienceId: string): Observable<Audience> {
|
|
84
|
+
if (!this.apiKey) {
|
|
85
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!audienceId) {
|
|
89
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return this.http.get<Audience>(
|
|
93
|
+
`${this.apiUrl}/api/audiences/${audienceId}`,
|
|
94
|
+
{ headers: this.getHeaders() },
|
|
95
|
+
this.retryCount,
|
|
96
|
+
this.retryDelay
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update an audience
|
|
102
|
+
*/
|
|
103
|
+
update(audienceId: string, options: UpdateAudienceOptions): Observable<Audience> {
|
|
104
|
+
if (!this.apiKey) {
|
|
105
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!audienceId) {
|
|
109
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.http.put<Audience>(
|
|
113
|
+
`${this.apiUrl}/api/audiences/${audienceId}`,
|
|
114
|
+
options,
|
|
115
|
+
{ headers: this.getHeaders() },
|
|
116
|
+
this.retryCount,
|
|
117
|
+
this.retryDelay
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Delete an audience
|
|
123
|
+
*/
|
|
124
|
+
delete(audienceId: string): Observable<void> {
|
|
125
|
+
if (!this.apiKey) {
|
|
126
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!audienceId) {
|
|
130
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this.http.delete<void>(
|
|
134
|
+
`${this.apiUrl}/api/audiences/${audienceId}`,
|
|
135
|
+
{ headers: this.getHeaders() },
|
|
136
|
+
this.retryCount,
|
|
137
|
+
this.retryDelay
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* List all audiences
|
|
143
|
+
*/
|
|
144
|
+
list(options?: PaginationOptions): Observable<AudienceListResponse> {
|
|
145
|
+
if (!this.apiKey) {
|
|
146
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let params = new HttpParams();
|
|
150
|
+
if (options?.page) {
|
|
151
|
+
params = params.set('page', options.page.toString());
|
|
152
|
+
}
|
|
153
|
+
if (options?.limit) {
|
|
154
|
+
params = params.set('limit', options.limit.toString());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.http.get<AudienceListResponse>(
|
|
158
|
+
`${this.apiUrl}/api/audiences`,
|
|
159
|
+
{
|
|
160
|
+
headers: this.getHeaders(),
|
|
161
|
+
params: params
|
|
162
|
+
},
|
|
163
|
+
this.retryCount,
|
|
164
|
+
this.retryDelay
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get audience statistics
|
|
170
|
+
*/
|
|
171
|
+
getStats(audienceId: string): Observable<AudienceStats> {
|
|
172
|
+
if (!this.apiKey) {
|
|
173
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!audienceId) {
|
|
177
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return this.http.get<AudienceStats>(
|
|
181
|
+
`${this.apiUrl}/api/audiences/${audienceId}/stats`,
|
|
182
|
+
{ headers: this.getHeaders() },
|
|
183
|
+
this.retryCount,
|
|
184
|
+
this.retryDelay
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Clean audience (remove bounced/unsubscribed contacts)
|
|
190
|
+
*/
|
|
191
|
+
clean(audienceId: string): Observable<{ removed: number }> {
|
|
192
|
+
if (!this.apiKey) {
|
|
193
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!audienceId) {
|
|
197
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return this.http.post<{ removed: number }>(
|
|
201
|
+
`${this.apiUrl}/api/audiences/${audienceId}/clean`,
|
|
202
|
+
{},
|
|
203
|
+
{ headers: this.getHeaders() },
|
|
204
|
+
this.retryCount,
|
|
205
|
+
this.retryDelay
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Merge audiences (source into target)
|
|
211
|
+
*/
|
|
212
|
+
merge(sourceAudienceId: string, targetAudienceId: string): Observable<Audience> {
|
|
213
|
+
if (!this.apiKey) {
|
|
214
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!sourceAudienceId || !targetAudienceId) {
|
|
218
|
+
return throwError(() => new ValidationError('Source and target audience IDs are required'));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return this.http.post<Audience>(
|
|
222
|
+
`${this.apiUrl}/api/audiences/${sourceAudienceId}/merge`,
|
|
223
|
+
{ targetAudienceId },
|
|
224
|
+
{ headers: this.getHeaders() },
|
|
225
|
+
this.retryCount,
|
|
226
|
+
this.retryDelay
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metigan Library Configuration
|
|
3
|
+
* Central configuration file for all modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default API URL for Metigan services
|
|
8
|
+
* All API calls are routed through this single endpoint
|
|
9
|
+
*
|
|
10
|
+
* Can be overridden with METIGAN_API_URL environment variable for testing
|
|
11
|
+
*/
|
|
12
|
+
export const API_URL = (typeof process !== 'undefined' && process.env?.['METIGAN_API_URL'])
|
|
13
|
+
|| 'https://api.metigan.com';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SDK Version
|
|
17
|
+
*/
|
|
18
|
+
export const SDK_VERSION = '1.0.0';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default timeout for API requests (in milliseconds)
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_TIMEOUT = 30000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default retry configuration
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_RETRY_COUNT = 3;
|
|
29
|
+
export const DEFAULT_RETRY_DELAY = 1000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Maximum file size for attachments (7MB)
|
|
33
|
+
*/
|
|
34
|
+
export const MAX_FILE_SIZE = 7 * 1024 * 1024;
|
|
35
|
+
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metigan Contacts Service
|
|
3
|
+
* Service for contact/subscriber management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Injectable } from '@angular/core';
|
|
7
|
+
import { Observable, throwError } from 'rxjs';
|
|
8
|
+
import { HttpParams } from '@angular/common/http';
|
|
9
|
+
import { MetiganHttpClient } from './http-client.service';
|
|
10
|
+
import { API_URL, DEFAULT_TIMEOUT, DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY } from './config';
|
|
11
|
+
import { ValidationError, MetiganError } from './errors';
|
|
12
|
+
import {
|
|
13
|
+
Contact,
|
|
14
|
+
CreateContactOptions,
|
|
15
|
+
UpdateContactOptions,
|
|
16
|
+
ContactListFilters,
|
|
17
|
+
ContactListResponse,
|
|
18
|
+
BulkContactResult
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
@Injectable({
|
|
22
|
+
providedIn: 'root'
|
|
23
|
+
})
|
|
24
|
+
export class MetiganContactsService {
|
|
25
|
+
private apiKey: string = '';
|
|
26
|
+
private apiUrl: string = API_URL;
|
|
27
|
+
private timeout: number = DEFAULT_TIMEOUT;
|
|
28
|
+
private retryCount: number = DEFAULT_RETRY_COUNT;
|
|
29
|
+
private retryDelay: number = DEFAULT_RETRY_DELAY;
|
|
30
|
+
|
|
31
|
+
constructor(private http: MetiganHttpClient) {}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the service with API key and options
|
|
35
|
+
*/
|
|
36
|
+
initialize(apiKey: string, options?: { apiUrl?: string; timeout?: number; retryCount?: number; retryDelay?: number }): void {
|
|
37
|
+
if (!apiKey) {
|
|
38
|
+
throw new MetiganError('API key is required');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.apiKey = apiKey;
|
|
42
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
43
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
44
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
45
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get default headers
|
|
50
|
+
*/
|
|
51
|
+
private getHeaders() {
|
|
52
|
+
return {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'x-api-key': this.apiKey,
|
|
55
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate email format
|
|
61
|
+
*/
|
|
62
|
+
private validateEmail(email: string): boolean {
|
|
63
|
+
if (!email || typeof email !== 'string') return false;
|
|
64
|
+
const parts = email.split('@');
|
|
65
|
+
if (parts.length !== 2) return false;
|
|
66
|
+
if (parts[0].length === 0) return false;
|
|
67
|
+
const domainParts = parts[1].split('.');
|
|
68
|
+
if (domainParts.length < 2) return false;
|
|
69
|
+
if (domainParts.some(part => part.length === 0)) return false;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create a new contact
|
|
75
|
+
*/
|
|
76
|
+
create(options: CreateContactOptions): Observable<Contact> {
|
|
77
|
+
if (!this.apiKey) {
|
|
78
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!options.email || !this.validateEmail(options.email)) {
|
|
82
|
+
return throwError(() => new ValidationError('Valid email is required'));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!options.audienceId) {
|
|
86
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this.http.post<Contact>(
|
|
90
|
+
`${this.apiUrl}/api/contacts`,
|
|
91
|
+
options,
|
|
92
|
+
{ headers: this.getHeaders() },
|
|
93
|
+
this.retryCount,
|
|
94
|
+
this.retryDelay
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get contact by ID
|
|
100
|
+
*/
|
|
101
|
+
get(contactId: string): Observable<Contact> {
|
|
102
|
+
if (!this.apiKey) {
|
|
103
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!contactId) {
|
|
107
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.http.get<Contact>(
|
|
111
|
+
`${this.apiUrl}/api/contacts/${contactId}`,
|
|
112
|
+
{ headers: this.getHeaders() },
|
|
113
|
+
this.retryCount,
|
|
114
|
+
this.retryDelay
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get contact by email
|
|
120
|
+
*/
|
|
121
|
+
getByEmail(email: string, audienceId: string): Observable<Contact> {
|
|
122
|
+
if (!this.apiKey) {
|
|
123
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!email || !this.validateEmail(email)) {
|
|
127
|
+
return throwError(() => new ValidationError('Valid email is required'));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!audienceId) {
|
|
131
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let params = new HttpParams();
|
|
135
|
+
params = params.set('email', email);
|
|
136
|
+
params = params.set('audienceId', audienceId);
|
|
137
|
+
|
|
138
|
+
return this.http.get<Contact>(
|
|
139
|
+
`${this.apiUrl}/api/contacts`,
|
|
140
|
+
{
|
|
141
|
+
headers: this.getHeaders(),
|
|
142
|
+
params: params
|
|
143
|
+
},
|
|
144
|
+
this.retryCount,
|
|
145
|
+
this.retryDelay
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Update a contact
|
|
151
|
+
*/
|
|
152
|
+
update(contactId: string, options: UpdateContactOptions): Observable<Contact> {
|
|
153
|
+
if (!this.apiKey) {
|
|
154
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!contactId) {
|
|
158
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this.http.put<Contact>(
|
|
162
|
+
`${this.apiUrl}/api/contacts/${contactId}`,
|
|
163
|
+
options,
|
|
164
|
+
{ headers: this.getHeaders() },
|
|
165
|
+
this.retryCount,
|
|
166
|
+
this.retryDelay
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Delete a contact
|
|
172
|
+
*/
|
|
173
|
+
delete(contactId: string): Observable<void> {
|
|
174
|
+
if (!this.apiKey) {
|
|
175
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!contactId) {
|
|
179
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return this.http.delete<void>(
|
|
183
|
+
`${this.apiUrl}/api/contacts/${contactId}`,
|
|
184
|
+
{ headers: this.getHeaders() },
|
|
185
|
+
this.retryCount,
|
|
186
|
+
this.retryDelay
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* List contacts with filters
|
|
192
|
+
*/
|
|
193
|
+
list(filters?: ContactListFilters): Observable<ContactListResponse> {
|
|
194
|
+
if (!this.apiKey) {
|
|
195
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let params = new HttpParams();
|
|
199
|
+
if (filters?.audienceId) {
|
|
200
|
+
params = params.set('audienceId', filters.audienceId);
|
|
201
|
+
}
|
|
202
|
+
if (filters?.status) {
|
|
203
|
+
params = params.set('status', filters.status);
|
|
204
|
+
}
|
|
205
|
+
if (filters?.tag) {
|
|
206
|
+
params = params.set('tag', filters.tag);
|
|
207
|
+
}
|
|
208
|
+
if (filters?.search) {
|
|
209
|
+
params = params.set('search', filters.search);
|
|
210
|
+
}
|
|
211
|
+
if (filters?.page) {
|
|
212
|
+
params = params.set('page', filters.page.toString());
|
|
213
|
+
}
|
|
214
|
+
if (filters?.limit) {
|
|
215
|
+
params = params.set('limit', filters.limit.toString());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return this.http.get<ContactListResponse>(
|
|
219
|
+
`${this.apiUrl}/api/contacts`,
|
|
220
|
+
{
|
|
221
|
+
headers: this.getHeaders(),
|
|
222
|
+
params: params
|
|
223
|
+
},
|
|
224
|
+
this.retryCount,
|
|
225
|
+
this.retryDelay
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Search contacts
|
|
231
|
+
*/
|
|
232
|
+
search(query: string, audienceId?: string): Observable<Contact[]> {
|
|
233
|
+
if (!this.apiKey) {
|
|
234
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!query) {
|
|
238
|
+
return throwError(() => new ValidationError('Search query is required'));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let params = new HttpParams();
|
|
242
|
+
params = params.set('search', query);
|
|
243
|
+
if (audienceId) {
|
|
244
|
+
params = params.set('audienceId', audienceId);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return this.http.get<Contact[]>(
|
|
248
|
+
`${this.apiUrl}/api/contacts/search`,
|
|
249
|
+
{
|
|
250
|
+
headers: this.getHeaders(),
|
|
251
|
+
params: params
|
|
252
|
+
},
|
|
253
|
+
this.retryCount,
|
|
254
|
+
this.retryDelay
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Bulk import contacts
|
|
260
|
+
*/
|
|
261
|
+
bulkImport(contacts: CreateContactOptions[], audienceId: string): Observable<BulkContactResult> {
|
|
262
|
+
if (!this.apiKey) {
|
|
263
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!contacts || contacts.length === 0) {
|
|
267
|
+
return throwError(() => new ValidationError('Contacts array is required'));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!audienceId) {
|
|
271
|
+
return throwError(() => new ValidationError('Audience ID is required'));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return this.http.post<BulkContactResult>(
|
|
275
|
+
`${this.apiUrl}/api/contacts/bulk`,
|
|
276
|
+
{ contacts, audienceId },
|
|
277
|
+
{ headers: this.getHeaders() },
|
|
278
|
+
this.retryCount,
|
|
279
|
+
this.retryDelay
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Subscribe a contact
|
|
285
|
+
*/
|
|
286
|
+
subscribe(contactId: string): Observable<Contact> {
|
|
287
|
+
if (!this.apiKey) {
|
|
288
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!contactId) {
|
|
292
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return this.http.post<Contact>(
|
|
296
|
+
`${this.apiUrl}/api/contacts/${contactId}/subscribe`,
|
|
297
|
+
{},
|
|
298
|
+
{ headers: this.getHeaders() },
|
|
299
|
+
this.retryCount,
|
|
300
|
+
this.retryDelay
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Unsubscribe a contact
|
|
306
|
+
*/
|
|
307
|
+
unsubscribe(contactId: string): Observable<Contact> {
|
|
308
|
+
if (!this.apiKey) {
|
|
309
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!contactId) {
|
|
313
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return this.http.post<Contact>(
|
|
317
|
+
`${this.apiUrl}/api/contacts/${contactId}/unsubscribe`,
|
|
318
|
+
{},
|
|
319
|
+
{ headers: this.getHeaders() },
|
|
320
|
+
this.retryCount,
|
|
321
|
+
this.retryDelay
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Add tags to a contact
|
|
327
|
+
*/
|
|
328
|
+
addTags(contactId: string, tags: string[]): Observable<Contact> {
|
|
329
|
+
if (!this.apiKey) {
|
|
330
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!contactId) {
|
|
334
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!tags || tags.length === 0) {
|
|
338
|
+
return throwError(() => new ValidationError('Tags array is required'));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return this.http.post<Contact>(
|
|
342
|
+
`${this.apiUrl}/api/contacts/${contactId}/tags`,
|
|
343
|
+
{ tags },
|
|
344
|
+
{ headers: this.getHeaders() },
|
|
345
|
+
this.retryCount,
|
|
346
|
+
this.retryDelay
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Remove tags from a contact
|
|
352
|
+
*/
|
|
353
|
+
removeTags(contactId: string, tags: string[]): Observable<Contact> {
|
|
354
|
+
if (!this.apiKey) {
|
|
355
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!contactId) {
|
|
359
|
+
return throwError(() => new ValidationError('Contact ID is required'));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (!tags || tags.length === 0) {
|
|
363
|
+
return throwError(() => new ValidationError('Tags array is required'));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return this.http.delete<Contact>(
|
|
367
|
+
`${this.apiUrl}/api/contacts/${contactId}/tags`,
|
|
368
|
+
{
|
|
369
|
+
headers: this.getHeaders(),
|
|
370
|
+
params: { tags: tags.join(',') }
|
|
371
|
+
},
|
|
372
|
+
this.retryCount,
|
|
373
|
+
this.retryDelay
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|