@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,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Client Service for Metigan SDK
|
|
3
|
+
* Wraps Angular HttpClient with retry logic and error handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Injectable } from '@angular/core';
|
|
7
|
+
import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '@angular/common/http';
|
|
8
|
+
import { Observable, throwError, timer } from 'rxjs';
|
|
9
|
+
import { catchError, retryWhen, mergeMap, map } from 'rxjs/operators';
|
|
10
|
+
import { ApiError, MetiganError } from './errors';
|
|
11
|
+
|
|
12
|
+
export interface HttpOptions {
|
|
13
|
+
headers?: HttpHeaders | { [header: string]: string | string[] };
|
|
14
|
+
params?: HttpParams | { [param: string]: any };
|
|
15
|
+
timeout?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable({
|
|
19
|
+
providedIn: 'root'
|
|
20
|
+
})
|
|
21
|
+
export class MetiganHttpClient {
|
|
22
|
+
constructor(private http: HttpClient) {}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Make GET request with retry logic
|
|
26
|
+
*/
|
|
27
|
+
get<T>(url: string, options?: HttpOptions, retryCount: number = 3, retryDelay: number = 1000): Observable<T> {
|
|
28
|
+
const opts = this.buildOptions(options);
|
|
29
|
+
|
|
30
|
+
return (this.http.get<T>(url, opts) as Observable<T>).pipe(
|
|
31
|
+
map((response: T) => response),
|
|
32
|
+
retryWhen(errors =>
|
|
33
|
+
errors.pipe(
|
|
34
|
+
mergeMap((error: HttpErrorResponse, index: number) => {
|
|
35
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
36
|
+
return timer(retryDelay * (index + 1));
|
|
37
|
+
}
|
|
38
|
+
return throwError(() => error);
|
|
39
|
+
})
|
|
40
|
+
)
|
|
41
|
+
),
|
|
42
|
+
catchError(this.handleError)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Make POST request with retry logic
|
|
48
|
+
*/
|
|
49
|
+
post<T>(url: string, body: any, options?: HttpOptions, retryCount: number = 3, retryDelay: number = 1000): Observable<T> {
|
|
50
|
+
const opts = this.buildOptions(options);
|
|
51
|
+
|
|
52
|
+
return (this.http.post<T>(url, body, opts) as Observable<T>).pipe(
|
|
53
|
+
map((response: T) => response),
|
|
54
|
+
retryWhen(errors =>
|
|
55
|
+
errors.pipe(
|
|
56
|
+
mergeMap((error: HttpErrorResponse, index: number) => {
|
|
57
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
58
|
+
return timer(retryDelay * (index + 1));
|
|
59
|
+
}
|
|
60
|
+
return throwError(() => error);
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
),
|
|
64
|
+
catchError(this.handleError)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Make PUT request with retry logic
|
|
70
|
+
*/
|
|
71
|
+
put<T>(url: string, body: any, options?: HttpOptions, retryCount: number = 3, retryDelay: number = 1000): Observable<T> {
|
|
72
|
+
const opts = this.buildOptions(options);
|
|
73
|
+
|
|
74
|
+
return (this.http.put<T>(url, body, opts) as Observable<T>).pipe(
|
|
75
|
+
map((response: T) => response),
|
|
76
|
+
retryWhen(errors =>
|
|
77
|
+
errors.pipe(
|
|
78
|
+
mergeMap((error: HttpErrorResponse, index: number) => {
|
|
79
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
80
|
+
return timer(retryDelay * (index + 1));
|
|
81
|
+
}
|
|
82
|
+
return throwError(() => error);
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
),
|
|
86
|
+
catchError(this.handleError)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Make DELETE request with retry logic
|
|
92
|
+
*/
|
|
93
|
+
delete<T>(url: string, options?: HttpOptions, retryCount: number = 3, retryDelay: number = 1000): Observable<T> {
|
|
94
|
+
const opts = this.buildOptions(options);
|
|
95
|
+
|
|
96
|
+
return (this.http.delete<T>(url, opts) as Observable<T>).pipe(
|
|
97
|
+
map((response: T) => response),
|
|
98
|
+
retryWhen(errors =>
|
|
99
|
+
errors.pipe(
|
|
100
|
+
mergeMap((error: HttpErrorResponse, index: number) => {
|
|
101
|
+
if (index < retryCount && this.shouldRetry(error)) {
|
|
102
|
+
return timer(retryDelay * (index + 1));
|
|
103
|
+
}
|
|
104
|
+
return throwError(() => error);
|
|
105
|
+
})
|
|
106
|
+
)
|
|
107
|
+
),
|
|
108
|
+
catchError(this.handleError)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build HTTP options with timeout
|
|
114
|
+
*/
|
|
115
|
+
private buildOptions(options?: HttpOptions): any {
|
|
116
|
+
const opts: any = {
|
|
117
|
+
observe: 'body' as const
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (options?.headers) {
|
|
121
|
+
opts.headers = options.headers;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (options?.params) {
|
|
125
|
+
opts.params = options.params;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Angular HttpClient doesn't support timeout directly
|
|
129
|
+
// Timeout should be handled at the interceptor level if needed
|
|
130
|
+
|
|
131
|
+
return opts;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Determine if error should trigger retry
|
|
136
|
+
*/
|
|
137
|
+
private shouldRetry(error: HttpErrorResponse): boolean {
|
|
138
|
+
// Retry on server errors (5xx) or network errors
|
|
139
|
+
return error.status >= 500 || error.status === 0 || !error.status;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Handle HTTP errors
|
|
144
|
+
*/
|
|
145
|
+
private handleError = (error: HttpErrorResponse): Observable<never> => {
|
|
146
|
+
if (error.error instanceof ErrorEvent) {
|
|
147
|
+
// Client-side error
|
|
148
|
+
return throwError(() => new MetiganError(`Network error: ${error.error.message}`));
|
|
149
|
+
} else {
|
|
150
|
+
// Server-side error
|
|
151
|
+
const message = error.error?.message || error.error?.error || `Request failed with status ${error.status}`;
|
|
152
|
+
return throwError(() => new ApiError(message, error.status));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metigan Angular Module
|
|
3
|
+
* Import this module in your Angular app to use Metigan services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { NgModule, ModuleWithProviders } from '@angular/core';
|
|
7
|
+
import { HttpClientModule } from '@angular/common/http';
|
|
8
|
+
import { MetiganService, METIGAN_CONFIG } from './metigan.service';
|
|
9
|
+
import { MetiganHttpClient } from './http-client.service';
|
|
10
|
+
import { MetiganEmailService } from './email.service';
|
|
11
|
+
import { MetiganFormsService } from './forms.service';
|
|
12
|
+
import { MetiganContactsService } from './contacts.service';
|
|
13
|
+
import { MetiganAudiencesService } from './audiences.service';
|
|
14
|
+
import { MetiganTemplatesService } from './templates.service';
|
|
15
|
+
import { MetiganClientOptions } from './types';
|
|
16
|
+
|
|
17
|
+
@NgModule({
|
|
18
|
+
imports: [HttpClientModule],
|
|
19
|
+
providers: [
|
|
20
|
+
MetiganHttpClient,
|
|
21
|
+
MetiganEmailService,
|
|
22
|
+
MetiganFormsService,
|
|
23
|
+
MetiganContactsService,
|
|
24
|
+
MetiganAudiencesService,
|
|
25
|
+
MetiganTemplatesService,
|
|
26
|
+
MetiganService
|
|
27
|
+
]
|
|
28
|
+
})
|
|
29
|
+
export class MetiganModule {
|
|
30
|
+
/**
|
|
31
|
+
* Configure Metigan with API key and options
|
|
32
|
+
* Call this in your root module's imports
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* imports: [
|
|
37
|
+
* MetiganModule.forRoot({
|
|
38
|
+
* apiKey: 'your-api-key'
|
|
39
|
+
* })
|
|
40
|
+
* ]
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
static forRoot(config: MetiganClientOptions): ModuleWithProviders<MetiganModule> {
|
|
44
|
+
return {
|
|
45
|
+
ngModule: MetiganModule,
|
|
46
|
+
providers: [
|
|
47
|
+
{
|
|
48
|
+
provide: METIGAN_CONFIG,
|
|
49
|
+
useValue: config
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Metigan Service
|
|
3
|
+
* Unified service that provides access to all Metigan modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Injectable, Inject, Optional } from '@angular/core';
|
|
7
|
+
import { MetiganEmailService } from './email.service';
|
|
8
|
+
import { MetiganFormsService } from './forms.service';
|
|
9
|
+
import { MetiganContactsService } from './contacts.service';
|
|
10
|
+
import { MetiganAudiencesService } from './audiences.service';
|
|
11
|
+
import { MetiganTemplatesService } from './templates.service';
|
|
12
|
+
import { MetiganError } from './errors';
|
|
13
|
+
import { MetiganClientOptions } from './types';
|
|
14
|
+
|
|
15
|
+
export const METIGAN_CONFIG = 'METIGAN_CONFIG';
|
|
16
|
+
|
|
17
|
+
@Injectable({
|
|
18
|
+
providedIn: 'root'
|
|
19
|
+
})
|
|
20
|
+
export class MetiganService {
|
|
21
|
+
public readonly email: MetiganEmailService;
|
|
22
|
+
public readonly forms: MetiganFormsService;
|
|
23
|
+
public readonly contacts: MetiganContactsService;
|
|
24
|
+
public readonly audiences: MetiganAudiencesService;
|
|
25
|
+
public readonly templates: MetiganTemplatesService;
|
|
26
|
+
|
|
27
|
+
private initialized: boolean = false;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
emailService: MetiganEmailService,
|
|
31
|
+
formsService: MetiganFormsService,
|
|
32
|
+
contactsService: MetiganContactsService,
|
|
33
|
+
audiencesService: MetiganAudiencesService,
|
|
34
|
+
templatesService: MetiganTemplatesService,
|
|
35
|
+
@Optional() @Inject(METIGAN_CONFIG) config?: MetiganClientOptions
|
|
36
|
+
) {
|
|
37
|
+
this.email = emailService;
|
|
38
|
+
this.forms = formsService;
|
|
39
|
+
this.contacts = contactsService;
|
|
40
|
+
this.audiences = audiencesService;
|
|
41
|
+
this.templates = templatesService;
|
|
42
|
+
|
|
43
|
+
// Auto-initialize if config is provided via injection
|
|
44
|
+
if (config) {
|
|
45
|
+
this.initialize(config);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initialize all services with API key and options
|
|
51
|
+
*/
|
|
52
|
+
initialize(options: MetiganClientOptions): void {
|
|
53
|
+
if (!options.apiKey) {
|
|
54
|
+
throw new MetiganError('API key is required');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const serviceOptions = {
|
|
58
|
+
apiUrl: options.apiUrl,
|
|
59
|
+
timeout: options.timeout,
|
|
60
|
+
retryCount: options.retryCount,
|
|
61
|
+
retryDelay: options.retryDelay
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.email.initialize(options.apiKey, serviceOptions);
|
|
65
|
+
this.forms.initialize(options.apiKey, serviceOptions);
|
|
66
|
+
this.contacts.initialize(options.apiKey, serviceOptions);
|
|
67
|
+
this.audiences.initialize(options.apiKey, serviceOptions);
|
|
68
|
+
this.templates.initialize(options.apiKey, serviceOptions);
|
|
69
|
+
|
|
70
|
+
this.initialized = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if services are initialized
|
|
75
|
+
*/
|
|
76
|
+
isInitialized(): boolean {
|
|
77
|
+
return this.initialized;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metigan Templates Service
|
|
3
|
+
* Service for managing email templates
|
|
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
|
+
EmailTemplate,
|
|
14
|
+
EmailTemplateListResponse,
|
|
15
|
+
PaginationOptions
|
|
16
|
+
} from './types';
|
|
17
|
+
|
|
18
|
+
@Injectable({
|
|
19
|
+
providedIn: 'root'
|
|
20
|
+
})
|
|
21
|
+
export class MetiganTemplatesService {
|
|
22
|
+
private apiKey: string = '';
|
|
23
|
+
private apiUrl: string = API_URL;
|
|
24
|
+
private timeout: number = DEFAULT_TIMEOUT;
|
|
25
|
+
private retryCount: number = DEFAULT_RETRY_COUNT;
|
|
26
|
+
private retryDelay: number = DEFAULT_RETRY_DELAY;
|
|
27
|
+
|
|
28
|
+
constructor(private http: MetiganHttpClient) {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize the service with API key and options
|
|
32
|
+
*/
|
|
33
|
+
initialize(apiKey: string, options?: { apiUrl?: string; timeout?: number; retryCount?: number; retryDelay?: number }): void {
|
|
34
|
+
if (!apiKey) {
|
|
35
|
+
throw new MetiganError('API key is required');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.apiKey = apiKey;
|
|
39
|
+
this.apiUrl = options?.apiUrl || API_URL;
|
|
40
|
+
this.timeout = options?.timeout || DEFAULT_TIMEOUT;
|
|
41
|
+
this.retryCount = options?.retryCount || DEFAULT_RETRY_COUNT;
|
|
42
|
+
this.retryDelay = options?.retryDelay || DEFAULT_RETRY_DELAY;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get default headers
|
|
47
|
+
*/
|
|
48
|
+
private getHeaders() {
|
|
49
|
+
return {
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
'x-api-key': this.apiKey,
|
|
52
|
+
'User-Agent': 'AngularSDK/1.0'
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List all templates
|
|
58
|
+
*/
|
|
59
|
+
list(options?: PaginationOptions): Observable<EmailTemplateListResponse> {
|
|
60
|
+
if (!this.apiKey) {
|
|
61
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let params = new HttpParams();
|
|
65
|
+
if (options?.page) {
|
|
66
|
+
params = params.set('page', options.page.toString());
|
|
67
|
+
}
|
|
68
|
+
if (options?.limit) {
|
|
69
|
+
params = params.set('limit', options.limit.toString());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return this.http.get<EmailTemplateListResponse>(
|
|
73
|
+
`${this.apiUrl}/api/templates`,
|
|
74
|
+
{
|
|
75
|
+
headers: this.getHeaders(),
|
|
76
|
+
params: params
|
|
77
|
+
},
|
|
78
|
+
this.retryCount,
|
|
79
|
+
this.retryDelay
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get template by ID
|
|
85
|
+
*/
|
|
86
|
+
get(templateId: string): Observable<EmailTemplate> {
|
|
87
|
+
if (!this.apiKey) {
|
|
88
|
+
return throwError(() => new MetiganError('Service not initialized. Call initialize() first.'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!templateId) {
|
|
92
|
+
return throwError(() => new ValidationError('Template ID is required'));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return this.http.get<EmailTemplate>(
|
|
96
|
+
`${this.apiUrl}/api/templates/${templateId}`,
|
|
97
|
+
{ headers: this.getHeaders() },
|
|
98
|
+
this.retryCount,
|
|
99
|
+
this.retryDelay
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|