@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.
Files changed (48) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +581 -0
  3. package/dist/LICENSE +22 -0
  4. package/dist/README.md +581 -0
  5. package/dist/esm2022/metigan-angular.mjs +5 -0
  6. package/dist/esm2022/public-api.mjs +21 -0
  7. package/dist/esm2022/src/lib/audiences.service.mjs +157 -0
  8. package/dist/esm2022/src/lib/config.mjs +30 -0
  9. package/dist/esm2022/src/lib/contacts.service.mjs +267 -0
  10. package/dist/esm2022/src/lib/email.service.mjs +267 -0
  11. package/dist/esm2022/src/lib/errors.mjs +40 -0
  12. package/dist/esm2022/src/lib/forms.service.mjs +180 -0
  13. package/dist/esm2022/src/lib/http-client.service.mjs +111 -0
  14. package/dist/esm2022/src/lib/metigan.module.mjs +67 -0
  15. package/dist/esm2022/src/lib/metigan.service.mjs +72 -0
  16. package/dist/esm2022/src/lib/templates.service.mjs +85 -0
  17. package/dist/esm2022/src/lib/types.mjs +6 -0
  18. package/dist/fesm2022/metigan-angular.mjs +1241 -0
  19. package/dist/fesm2022/metigan-angular.mjs.map +1 -0
  20. package/dist/index.d.ts +5 -0
  21. package/dist/public-api.d.ts +15 -0
  22. package/dist/src/lib/audiences.service.d.ts +62 -0
  23. package/dist/src/lib/config.d.ts +28 -0
  24. package/dist/src/lib/contacts.service.d.ts +80 -0
  25. package/dist/src/lib/email.service.d.ts +44 -0
  26. package/dist/src/lib/errors.d.ts +24 -0
  27. package/dist/src/lib/forms.service.d.ts +67 -0
  28. package/dist/src/lib/http-client.service.d.ts +46 -0
  29. package/dist/src/lib/metigan.module.d.ts +27 -0
  30. package/dist/src/lib/metigan.service.d.ts +27 -0
  31. package/dist/src/lib/templates.service.d.ts +36 -0
  32. package/dist/src/lib/types.d.ts +329 -0
  33. package/examples/basic.component.ts +113 -0
  34. package/ng-package.json +8 -0
  35. package/package.json +68 -0
  36. package/public-api.ts +26 -0
  37. package/src/lib/audiences.service.ts +230 -0
  38. package/src/lib/config.ts +35 -0
  39. package/src/lib/contacts.service.ts +377 -0
  40. package/src/lib/email.service.ts +286 -0
  41. package/src/lib/errors.ts +45 -0
  42. package/src/lib/forms.service.ts +263 -0
  43. package/src/lib/http-client.service.ts +156 -0
  44. package/src/lib/metigan.module.ts +55 -0
  45. package/src/lib/metigan.service.ts +80 -0
  46. package/src/lib/templates.service.ts +103 -0
  47. package/src/lib/types.ts +398 -0
  48. 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
+