@corp-products/app-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +3 -0
  2. package/eslint.config.js +48 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +20 -0
  5. package/project.json +29 -0
  6. package/src/index.ts +9 -0
  7. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.html +7 -0
  8. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.scss +8 -0
  9. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.ts +125 -0
  10. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.interface.ts +15 -0
  11. package/src/lib/core/components/app-header/app-header.component.html +26 -0
  12. package/src/lib/core/components/app-header/app-header.component.scss +0 -0
  13. package/src/lib/core/components/app-header/app-header.component.ts +48 -0
  14. package/src/lib/core/components/app-side-menu/app-side-menu.component.html +20 -0
  15. package/src/lib/core/components/app-side-menu/app-side-menu.component.scss +0 -0
  16. package/src/lib/core/components/app-side-menu/app-side-menu.component.ts +30 -0
  17. package/src/lib/core/components/app-side-menu/routes-names.ts +28 -0
  18. package/src/lib/core/components/app-side-menu/side-menu-items.ts +45 -0
  19. package/src/lib/core/components/app-side-menu/side-menu.ts +12 -0
  20. package/src/lib/core/components/global-loader/global-loader.component.html +18 -0
  21. package/src/lib/core/components/global-loader/global-loader.component.scss +0 -0
  22. package/src/lib/core/components/global-loader/global-loader.component.spec.ts +24 -0
  23. package/src/lib/core/components/global-loader/global-loader.component.ts +16 -0
  24. package/src/lib/core/components/index.ts +4 -0
  25. package/src/lib/core/components/no-access/no-access.component.html +10 -0
  26. package/src/lib/core/components/no-access/no-access.component.scss +0 -0
  27. package/src/lib/core/components/no-access/no-access.component.ts +19 -0
  28. package/src/lib/core/core-config.ts +11 -0
  29. package/src/lib/core/directives/index.ts +1 -0
  30. package/src/lib/core/directives/permissions.directive.ts +114 -0
  31. package/src/lib/core/enums/index.ts +1 -0
  32. package/src/lib/core/enums/user-permissions.enum.ts +36 -0
  33. package/src/lib/core/guards/auth.guard.ts +17 -0
  34. package/src/lib/core/guards/index.ts +2 -0
  35. package/src/lib/core/guards/permissions.guard.ts +17 -0
  36. package/src/lib/core/handlers/http-context-handler.ts +9 -0
  37. package/src/lib/core/handlers/storage-handler.ts +37 -0
  38. package/src/lib/core/handlers/stortage.ts +7 -0
  39. package/src/lib/core/interceptors/app-http-config.ts +8 -0
  40. package/src/lib/core/interceptors/base-http.interceptor.ts +44 -0
  41. package/src/lib/core/interceptors/global-http-error.interceptor.ts +21 -0
  42. package/src/lib/core/interceptors/index.ts +3 -0
  43. package/src/lib/core/interfaces/index.ts +3 -0
  44. package/src/lib/core/interfaces/jwt-token-decoded.interface.ts +7 -0
  45. package/src/lib/core/interfaces/list-response.interface.ts +5 -0
  46. package/src/lib/core/interfaces/router-data.interface.ts +20 -0
  47. package/src/lib/core/interfaces/user-info.interface.ts +4 -0
  48. package/src/lib/core/interfaces/user-profile-data.ts +19 -0
  49. package/src/lib/core/services/auth.service.ts +63 -0
  50. package/src/lib/core/services/base-http-service/base-http-response.ts +14 -0
  51. package/src/lib/core/services/base-http-service/base-http-types.ts +42 -0
  52. package/src/lib/core/services/base-http-service/base-http-utils.ts +48 -0
  53. package/src/lib/core/services/base-http-service/base-http.service.ts +106 -0
  54. package/src/lib/core/services/base-http-service/http-context-handler.ts +9 -0
  55. package/src/lib/core/services/base-http-service/index.ts +4 -0
  56. package/src/lib/core/services/base-pagination-service/base-pagination.service.ts +59 -0
  57. package/src/lib/core/services/base-pagination-service/index.ts +3 -0
  58. package/src/lib/core/services/base-pagination-service/list-options.interface.ts +8 -0
  59. package/src/lib/core/services/base-pagination-service/pagination.ts +6 -0
  60. package/src/lib/core/services/enviroment.base.service.ts +16 -0
  61. package/src/lib/core/services/error-handler.service.ts +74 -0
  62. package/src/lib/core/services/index.ts +8 -0
  63. package/src/lib/core/services/jwt-decoder.service.ts +23 -0
  64. package/src/lib/core/services/loader.service.ts +32 -0
  65. package/src/lib/core/services/lov.service.ts +22 -0
  66. package/src/lib/core/services/permissions.service.ts +66 -0
  67. package/src/lib/core/services/toaster.service.ts +65 -0
  68. package/tsconfig.json +26 -0
  69. package/tsconfig.lib.json +11 -0
  70. package/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,114 @@
1
+ import {
2
+ Directive,
3
+ Input,
4
+ OnDestroy,
5
+ OnInit,
6
+ TemplateRef,
7
+ ViewContainerRef,
8
+ } from '@angular/core';
9
+ import { PermissionsService } from '../services';
10
+ import { PermissionsActions, UserPermissionsEnum } from '../enums';
11
+ import { Subscription } from 'rxjs';
12
+
13
+ @Directive({
14
+ selector: '[hasPermissions]',
15
+ standalone: true
16
+ })
17
+ export class HasPermissionsDirective implements OnInit, OnDestroy {
18
+ private _actions: string[];
19
+ private _key: string;
20
+ private _newPermissions: string[];
21
+ private _isDomain: boolean;
22
+
23
+ private isViewCreated = false;
24
+ private subscription: Subscription;
25
+
26
+ constructor(
27
+ private permissionsService: PermissionsService,
28
+ private viewContainer: ViewContainerRef,
29
+ private templateRef: TemplateRef<unknown>
30
+ ) {}
31
+
32
+ /**
33
+ * make sure that the key value looks like the UserPermissionsEnum enum
34
+ * usage: *permissions="[PermissionsActions.EDIT]; key: LOOKUPS
35
+ */
36
+
37
+ @Input({ required: true }) set hasPermissions(actions: string[]) {
38
+ if (this.arraysChanged(this._actions, actions)) {
39
+ this._actions = actions;
40
+ this.updateView();
41
+ }
42
+ }
43
+
44
+ @Input({ required: true }) set hasPermissionsKey(key: string) {
45
+ if (this._key !== key) {
46
+ this._key = key;
47
+ this.updateView();
48
+ }
49
+ }
50
+
51
+ @Input() set hasPermissionsNewPermissions(newPermissions: string[]) {
52
+ if (this.arraysChanged(this._newPermissions, newPermissions)) {
53
+ this._newPermissions = newPermissions;
54
+ this.updateView();
55
+ }
56
+ }
57
+
58
+ @Input() set hasPermissionsIsDomain(isDomain: boolean) {
59
+ if (this._isDomain !== isDomain) {
60
+ this._isDomain = isDomain;
61
+ this.updateView();
62
+ }
63
+ }
64
+
65
+ ngOnInit(): void {
66
+ this.subscription = this.permissionsService.domainPermissions$.subscribe(() => {
67
+ if (this._isDomain) {
68
+ this.updateView();
69
+ }
70
+ });
71
+ }
72
+
73
+ ngOnDestroy(): void {
74
+ this.subscription?.unsubscribe();
75
+ }
76
+
77
+ private updateView() {
78
+ const canShow = this._actions.some(action => this.hasPermission(action));
79
+
80
+ if (canShow && !this.isViewCreated) {
81
+ this.viewContainer.createEmbeddedView(this.templateRef);
82
+ this.isViewCreated = true;
83
+ } else if (!canShow && this.isViewCreated) {
84
+ this.clearView();
85
+ }
86
+ }
87
+
88
+ private hasPermission(action: string): boolean {
89
+ if (this._isDomain) {
90
+ return this.permissionsService.checkDomainHasPermission(
91
+ this._key as UserPermissionsEnum,
92
+ action as PermissionsActions
93
+ );
94
+ }
95
+
96
+ return this.permissionsService.checkKeyHasPermission(
97
+ this._key as UserPermissionsEnum,
98
+ action as PermissionsActions,
99
+ this._newPermissions
100
+ );
101
+ }
102
+
103
+ private clearView() {
104
+ this.viewContainer.clear();
105
+ this.isViewCreated = false;
106
+ }
107
+
108
+ private arraysChanged(a?: string[], b?: string[]): boolean {
109
+ if (a === b) return false;
110
+ if (!a || !b) return true;
111
+ if (a.length !== b.length) return true;
112
+ return a.some((v, i) => v !== b[i]);
113
+ }
114
+ }
@@ -0,0 +1 @@
1
+ export * from './user-permissions.enum';
@@ -0,0 +1,36 @@
1
+ export enum UserPermissionsEnum {
2
+ BOD = "BOD",
3
+ BOARD_COMMITTEES = "BOARD_COMMITTEES",
4
+ BOARD_MEETINGS = "BOARD_MEETINGS",
5
+ MEETING_INSIDE_THE_BOD = "MEETING_INSIDE_THE_BOD",
6
+ COMMITTEE = "COMMITTEE",
7
+ MEETING_INSIDE_THE_COMMITTEE = "MEETING_INSIDE_THE_COMMITTEE",
8
+ MEETING = "MEETING",
9
+ DECISION = "DECISION",
10
+ RECOMMENDATION = "RECOMMENDATION",
11
+ TASK = "TASK",
12
+ VOTING = "VOTING",
13
+ VOTING_SECRETARY_RESULTS = "VOTING_SECRETARY_RESULTS",
14
+ VOTING_RESULTS = "VOTING_RESULTS",
15
+ DRAFT_MOM = "DRAFT_MOM",
16
+ FINAL_MOM = "FINAL_MOM",
17
+ MOM = "MOM",
18
+ AGENDA = "AGENDA",
19
+ ATTENDEES = "ATTENDEES",
20
+ COMMENT = "COMMENT"
21
+ }
22
+
23
+ export enum PermissionsActions {
24
+ VIEW = "VIEW",
25
+ CREATE = "CREATE",
26
+ EDIT = "EDIT",
27
+ DELETE = "DELETE",
28
+ REORDER = "REORDER",
29
+ DOWNLOAD = "DOWNLOAD",
30
+ MARK = "MARK",
31
+ GENERATE = "GENERATE",
32
+ TERMINATE = "TERMINATE",
33
+ START = "START",
34
+ END = "END",
35
+ ACTIVATE = "ACTIVATE"
36
+ }
@@ -0,0 +1,17 @@
1
+ import { inject } from "@angular/core";
2
+ import { CanActivateFn } from "@angular/router";
3
+ import { AuthService } from "../services";
4
+ import { CORE_CONFIG, CoreConfig } from "../core-config";
5
+
6
+ export const authGuard: CanActivateFn = () => {
7
+ const appConfig = inject<CoreConfig>(CORE_CONFIG);
8
+ const authService = inject(AuthService);
9
+ const token: string | null = authService.getUserToken();
10
+
11
+ if (!token) {
12
+ window.location.href = appConfig.loginUrl;
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ };
@@ -0,0 +1,2 @@
1
+ export * from './auth.guard';
2
+ export * from './permissions.guard';
@@ -0,0 +1,17 @@
1
+ import { inject } from "@angular/core";
2
+ import { CanActivateFn, Router } from "@angular/router";
3
+ import { PermissionsService } from "../services";
4
+ import { RoutePermissionsInfo } from "../interfaces";
5
+
6
+ export const permissionsGuard: CanActivateFn = (route) => {
7
+ const permissionsService = inject(PermissionsService);
8
+ const router = inject(Router);
9
+ const { key, action }: RoutePermissionsInfo = route.data["permissions"];
10
+
11
+ if (!route.data["permissions"] || (route.data["permissions"] && permissionsService.routeGuardChecker(key, action))) {
12
+ return true;
13
+ } else {
14
+ router.navigate(["no-access"]);
15
+ return false;
16
+ }
17
+ };
@@ -0,0 +1,9 @@
1
+ import { HttpContext, HttpContextToken } from '@angular/common/http';
2
+
3
+ export const IS_SYSTEM_LOADER = new HttpContextToken<boolean>(() => true); // set default loader
4
+
5
+ export class HttpContextHandler {
6
+ static setLoaderType(isSystemLoader: boolean) {
7
+ return new HttpContext().set(IS_SYSTEM_LOADER, isSystemLoader);
8
+ }
9
+ }
@@ -0,0 +1,37 @@
1
+ // Important for global context
2
+ import { LocalStorageKeys, SessionStorageKeys, StorageEnum } from "./stortage";
3
+
4
+ const setLocalStorage = (key: string, value: string) => localStorage.setItem(key, value);
5
+ const getLocalStorage = (key: string) => localStorage.getItem(key);
6
+ const clearLocalStorage = () => localStorage.clear();
7
+ const setSessionStorage = (key: string, value: string) => sessionStorage.setItem(key, value);
8
+ const getSessionStorage = (key: string) => sessionStorage.getItem(key);
9
+ const clearSessionStorage = () => sessionStorage.clear();
10
+
11
+ export class StorageHandler {
12
+ public static local = {
13
+ set: (key: LocalStorageKeys, value: unknown) => this.setItem(key, value, setLocalStorage),
14
+ get: <T>(key: LocalStorageKeys) => this.getItem<T>(key, getLocalStorage),
15
+ clear: () => this.clearStorage(clearLocalStorage)
16
+ };
17
+ public static session = {
18
+ set: (key: SessionStorageKeys, value: unknown) => this.setItem(key, value, setSessionStorage),
19
+ get: <T>(key: SessionStorageKeys) => this.getItem<T>(key, getSessionStorage),
20
+ clear: () => this.clearStorage(clearSessionStorage)
21
+ };
22
+
23
+ private static setItem(key: StorageEnum, value: unknown, set: (key: string, value: string) => void): void {
24
+ if (!key) return;
25
+ set(key as string, typeof value === "string" ? value : JSON.stringify(value));
26
+ }
27
+
28
+ private static getItem<T>(key: StorageEnum, get: (key: string) => string | null, withParsing = false): T | null {
29
+ const data: string | null = get(key as string) ?? null;
30
+ if (data) return withParsing ? JSON.parse(data) : data;
31
+ return null;
32
+ }
33
+
34
+ private static clearStorage(clear: () => void): void {
35
+ clear();
36
+ }
37
+ }
@@ -0,0 +1,7 @@
1
+ export enum LocalStorageKeys {
2
+ LANGUAGE = "LANGUAGE",
3
+ TOKEN = "TOKEN"
4
+ }
5
+
6
+ export enum SessionStorageKeys {}
7
+ export type StorageEnum = SessionStorageKeys | LocalStorageKeys;
@@ -0,0 +1,8 @@
1
+ export const COMMON_BASE_URL = "common/api/v1";
2
+ export const APP_HTTP_CONFIG = {
3
+ BOARD_BASE_URL: "board-acl/private/v1",
4
+ MEETING_BASE_URL: "meeting-acl/private/v1",
5
+ IGATE_BASE_URL: COMMON_BASE_URL + "/igate-users",
6
+ LOV_BASE_URL: COMMON_BASE_URL + "/lov",
7
+ ATTACHMENT_BASE_URL: COMMON_BASE_URL + "/attachment"
8
+ };
@@ -0,0 +1,44 @@
1
+ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
2
+ import { Inject, inject, Injectable } from "@angular/core";
3
+ import { finalize, Observable, tap } from "rxjs";
4
+ import { IS_SYSTEM_LOADER } from "../handlers/http-context-handler";
5
+ import { AuthService } from "../services/auth.service";
6
+ import { LoaderService } from "../services/loader.service";
7
+ import { CORE_CONFIG, CoreConfig } from "../core-config";
8
+
9
+ @Injectable()
10
+ export class HttpBaseInterceptor implements HttpInterceptor {
11
+ authService = inject(AuthService);
12
+ loaderService = inject(LoaderService);
13
+
14
+ constructor(@Inject(CORE_CONFIG) protected appConfig: CoreConfig) {}
15
+
16
+ intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
17
+ console.log("interceptor", this.appConfig);
18
+ const URL = `${this.appConfig.gatewayUrl}${request.url}`;
19
+ const token = this.authService.getUserToken();
20
+
21
+ // Handler header
22
+ request = request.clone({
23
+ url: URL,
24
+ setHeaders: {
25
+ Authorization: `Bearer ${token}`,
26
+ Language: "en"
27
+ }
28
+ });
29
+
30
+ // Handle loader
31
+ const IS_SYSTEM_LOADER_CHECK = request.context.get(IS_SYSTEM_LOADER);
32
+ this.loaderService.setLoading(true, IS_SYSTEM_LOADER_CHECK, request.url);
33
+
34
+ return next.handle(request).pipe(
35
+ // Handler Token
36
+ tap((event) => {
37
+ // if (event instanceof HttpResponse && request.url.includes(User_Token)) {
38
+ // Set user token ..
39
+ // }
40
+ }),
41
+ finalize(() => this.loaderService.setLoading(false, IS_SYSTEM_LOADER_CHECK, request.url))
42
+ );
43
+ }
44
+ }
@@ -0,0 +1,21 @@
1
+ import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
2
+ import { Observable, retry, timer } from 'rxjs';
3
+
4
+ export class GlobalHttpErrorInterceptor implements HttpInterceptor {
5
+ // check if it is a server error to retry the request
6
+ shouldRetry(error: HttpErrorResponse) {
7
+ if (error.status >= 500) {
8
+ return timer(1000);
9
+ }
10
+ throw error;
11
+ }
12
+
13
+ intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
14
+ return next.handle(request).pipe(
15
+ retry({
16
+ count: 1,
17
+ delay: this.shouldRetry
18
+ })
19
+ );
20
+ }
21
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./app-http-config";
2
+ export * from "./base-http.interceptor";
3
+ export * from "./global-http-error.interceptor";
@@ -0,0 +1,3 @@
1
+ export * from "./router-data.interface"
2
+ export * from "./list-response.interface"
3
+ export * from "./user-info.interface"
@@ -0,0 +1,7 @@
1
+ import { JwtPayload } from "jwt-decode";
2
+ import { UserProfileData } from "./user-profile-data";
3
+ export interface JWTDecoded extends JwtPayload {
4
+ permissions: string[];
5
+ role: string;
6
+ profile: UserProfileData;
7
+ }
@@ -0,0 +1,5 @@
1
+ export interface ListResponse<T> {
2
+ data: T;
3
+ totalElements: number;
4
+ totalPages: number;
5
+ }
@@ -0,0 +1,20 @@
1
+ import { Route } from "@angular/router";
2
+ import {PermissionsActions, UserPermissionsEnum} from "../enums";
3
+
4
+ export interface RouteData {
5
+ breadcrumb?: ((data: RouteData) => string) | string;
6
+ showBreadcrumb?: boolean;
7
+ isClickableInBreadcrumb?: boolean;
8
+ permissions?: RoutePermissionsInfo;
9
+
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ [key: string]: any;
12
+ }
13
+ export interface RoutePermissionsInfo {
14
+ key: UserPermissionsEnum;
15
+ action: PermissionsActions;
16
+ }
17
+ export interface AppRoute extends Route {
18
+ data?: RouteData;
19
+ children?: AppRoute[];
20
+ }
@@ -0,0 +1,4 @@
1
+ export interface UserInfo {
2
+ userName:string;
3
+ email:string;
4
+ }
@@ -0,0 +1,19 @@
1
+ export interface UserProfileData {
2
+ contact: Contact;
3
+ position: Position;
4
+ email: string;
5
+ profileImage: string;
6
+ nameEn: string;
7
+ nameAr: string;
8
+ }
9
+
10
+ export interface Contact {
11
+ mobile: string;
12
+ work: string;
13
+ }
14
+
15
+ export interface Position {
16
+ code: string;
17
+ name: string;
18
+ }
19
+
@@ -0,0 +1,63 @@
1
+ import { Inject, Injectable } from "@angular/core";
2
+ import { BehaviorSubject } from "rxjs";
3
+ import { StorageHandler } from "../handlers/storage-handler";
4
+ import { LocalStorageKeys } from "../handlers/stortage";
5
+ import { BaseHttpService, HttpConfig } from "./base-http-service";
6
+ import { CORE_CONFIG, CoreConfig } from "../core-config";
7
+
8
+ @Injectable({
9
+ providedIn: "root"
10
+ })
11
+ export class AuthService extends BaseHttpService {
12
+ public isUserLoggedIn$ = new BehaviorSubject(this.isLoggedIn());
13
+
14
+ constructor(@Inject(CORE_CONFIG) protected appConfig: CoreConfig) {
15
+ super();
16
+ }
17
+
18
+ override setApiConfig(): HttpConfig {
19
+ return {
20
+ apiUrl: "",
21
+ microServiceUrl: ""
22
+ };
23
+ }
24
+
25
+ initAuthentication() {
26
+ const token: string | null = new URLSearchParams(window.location.search).get("token");
27
+ if (token) {
28
+ this.setAuthentication(token);
29
+ window.location.search = "";
30
+ }
31
+ }
32
+
33
+ setAuthentication(token: string) {
34
+ this.isUserLoggedIn$.next(true);
35
+ this.setUserToken(token);
36
+ }
37
+
38
+ isLoggedIn(): boolean {
39
+ return !!this.getUserToken();
40
+ }
41
+
42
+ getUserToken(): string | null {
43
+ return StorageHandler.local.get(LocalStorageKeys.TOKEN);
44
+ }
45
+
46
+ setUserToken(token?: string) {
47
+ return StorageHandler.local.set(LocalStorageKeys.TOKEN, token);
48
+ }
49
+
50
+ logoutFromSSO() {
51
+ const logoutUrl = this.appConfig.logoutEndpoint;
52
+ return this.single<unknown>("", {
53
+ urlRewrite: logoutUrl
54
+ });
55
+ }
56
+
57
+ clearAuth() {
58
+ StorageHandler.local.clear();
59
+ StorageHandler.session.clear();
60
+ this.isUserLoggedIn$.next(false);
61
+ window.location.href = this.appConfig.loginUrl;
62
+ }
63
+ }
@@ -0,0 +1,14 @@
1
+ export interface BaseHttpResponse<T> {
2
+ code: number;
3
+ errors: BaseError;
4
+ payload: T;
5
+ serviceTime: string;
6
+ success: boolean;
7
+ }
8
+
9
+ interface BaseError {
10
+ errorCode: string;
11
+ type: string;
12
+ timestamp: string;
13
+ param: string[];
14
+ }
@@ -0,0 +1,42 @@
1
+ import { HttpContext, HttpHeaders, HttpParams } from '@angular/common/http';
2
+ import { Params as RouterParams } from '@angular/router';
3
+
4
+ export interface HttpConfig {
5
+ apiUrl?: string;
6
+ microServiceUrl:string;
7
+ globalMapFn?: (res: any) => any;
8
+ methods?: ServiceConfig;
9
+ }
10
+
11
+ export type Params =
12
+ | HttpParams
13
+ | { [param: string]: string | string[] | unknown };
14
+ export type HttpRequestOptions = {
15
+ headers?:
16
+ | HttpHeaders
17
+ | RouterParams
18
+ | { [header: string]: string | string[] };
19
+ params?: HttpParams | { [param: string]: string | string[] } | Params;
20
+ context?: HttpContext;
21
+ reportProgress?: boolean;
22
+ observe?: 'events';
23
+ responseType?: string;
24
+ };
25
+ export const HttpRequestOptionsKeys: Array<keyof Required<HttpRequestOptions>> =
26
+ ['headers', 'params', 'context', 'reportProgress', 'observe', 'responseType'];
27
+
28
+ export type HttpOptions<T = any> = HttpRequestOptions & {
29
+ urlRewrite?: string;
30
+ urlPostfix?: string;
31
+ mapFn?: (res: any) => T;
32
+ };
33
+
34
+ export interface ServiceConfig {
35
+ single?: HttpOptions;
36
+ getAll?: HttpOptions;
37
+ add?: HttpOptions;
38
+ update?: HttpOptions;
39
+ delete?: HttpOptions;
40
+ }
41
+
42
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
@@ -0,0 +1,48 @@
1
+ import { map } from 'rxjs/operators';
2
+ import { HttpOptions, HttpRequestOptionsKeys } from './base-http-types';
3
+ import { BaseHttpResponse } from './base-http-response';
4
+
5
+ export const resolveUrl = (
6
+ baseUrl: string,
7
+ options?: HttpOptions,
8
+ ...args: string[]
9
+ ): string => {
10
+ const { urlRewrite, urlPostfix } = options || {};
11
+
12
+ if (urlRewrite) {
13
+ return urlRewrite;
14
+ }
15
+
16
+ let result = baseUrl;
17
+
18
+ if (args && args.length > 0 && args[0] !== '') {
19
+ result += `/${args.join('/')}`;
20
+ }
21
+
22
+ if (urlPostfix) {
23
+ result += `/${urlPostfix}`;
24
+ }
25
+
26
+ return result;
27
+ };
28
+
29
+ export const extractRequestOptions = (options?: any) => {
30
+ if (!options || typeof options !== 'object') {
31
+ return {};
32
+ }
33
+ /* eslint-disable */
34
+ return HttpRequestOptionsKeys.reduce((requestOptions: any, key) => {
35
+ const value = options[key];
36
+
37
+ if (value !== undefined) {
38
+ requestOptions[key] = value;
39
+ }
40
+
41
+ return requestOptions;
42
+ }, {});
43
+ };
44
+
45
+ export const mapResponse = <T>(options?: HttpOptions) =>
46
+ map((res: T) => (options?.mapFn ? options.mapFn(res) : res));
47
+
48
+ export const epmDefaultMapper = <T>(res: BaseHttpResponse<T>) => res.payload;
@@ -0,0 +1,106 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { inject } from '@angular/core';
3
+ import { Observable } from 'rxjs';
4
+ import { HttpConfig, HttpOptions, ServiceConfig } from './base-http-types';
5
+ import { epmDefaultMapper, extractRequestOptions, mapResponse, resolveUrl } from './base-http-utils';
6
+
7
+ export abstract class BaseHttpService {
8
+ private readonly http: HttpClient = inject(HttpClient);
9
+
10
+ get url(): string {
11
+ const { apiUrl,microServiceUrl } = this.setApiConfig();
12
+ return `${microServiceUrl}${apiUrl ? `/${apiUrl}` : ''}`;
13
+ }
14
+
15
+ get methodsConfig(): ServiceConfig | undefined {
16
+ const { methods } = this.setApiConfig();
17
+ return methods;
18
+ }
19
+
20
+ get globalMapFn(): (res: any) => any {
21
+ const { globalMapFn } = this.setApiConfig();
22
+ return globalMapFn || epmDefaultMapper;
23
+ }
24
+
25
+ abstract setApiConfig(): HttpConfig;
26
+
27
+ single<T>(id: string | number, options?: HttpOptions): Observable<T> {
28
+ const activeOptions = options || this.getMethodConfig('single');
29
+ const url = resolveUrl(this.url, activeOptions, id?.toString());
30
+ const requestOptions = extractRequestOptions(activeOptions);
31
+
32
+ return this.http
33
+ .get<T>(url, requestOptions)
34
+ .pipe(mapResponse(this.getMapFun(activeOptions)));
35
+ }
36
+
37
+ getAll<T>(options?: HttpOptions): Observable<T> {
38
+ const activeOptions = options || this.getMethodConfig('getAll');
39
+ const url = resolveUrl(this.url, activeOptions);
40
+ const requestOptions = extractRequestOptions(activeOptions);
41
+
42
+ return this.http
43
+ .get<T>(url, requestOptions)
44
+ .pipe(mapResponse(this.getMapFun(activeOptions)));
45
+ }
46
+
47
+ add<T>(body: unknown, options?: HttpOptions): Observable<T> {
48
+ const activeOptions = options || this.getMethodConfig('add');
49
+ const url = resolveUrl(this.url, activeOptions);
50
+ const requestOptions = { ...extractRequestOptions(activeOptions) };
51
+
52
+ return this.http
53
+ .post<T>(url, body, requestOptions)
54
+ .pipe(mapResponse(this.getMapFun(activeOptions)));
55
+ }
56
+
57
+ update<T>(
58
+ id: string | number,
59
+ body: unknown,
60
+ options?: HttpOptions
61
+ ): Observable<T> {
62
+ const activeOptions = options || this.getMethodConfig('update');
63
+ const url = resolveUrl(this.url, activeOptions, id?.toString());
64
+ const requestOptions = { ...extractRequestOptions(activeOptions) };
65
+
66
+ return this.http
67
+ .put<T>(url, body, requestOptions)
68
+ .pipe(mapResponse(this.getMapFun(activeOptions)));
69
+ }
70
+
71
+ delete<T>(id: string | number, options?: HttpOptions): Observable<T> {
72
+ const activeOptions = options || this.getMethodConfig('delete');
73
+ const url = resolveUrl(this.url, activeOptions, id?.toString());
74
+ const requestOptions = extractRequestOptions(activeOptions);
75
+
76
+ return this.http
77
+ .delete<T>(url, requestOptions)
78
+ .pipe(mapResponse(this.getMapFun(activeOptions)));
79
+ }
80
+
81
+ protected getMethodConfig(method: keyof ServiceConfig): any {
82
+ return this.methodsConfig ? this.methodsConfig[method] : undefined;
83
+ }
84
+
85
+ protected getMapFun(options?: HttpOptions) {
86
+ const getMapFunObj = {
87
+ default: { mapFn: epmDefaultMapper },
88
+ custom: { mapFn: this.globalMapFn },
89
+ };
90
+
91
+ if (
92
+ options &&
93
+ (options?.reportProgress || options?.responseType === 'blob')
94
+ ) {
95
+ return { mapFn: (res: unknown) => res };
96
+ }
97
+
98
+ if (options && options?.mapFn) {
99
+ return { mapFn: options.mapFn };
100
+ }
101
+
102
+ const isGlobalMapFnValid =
103
+ this.globalMapFn && typeof this.globalMapFn === 'function';
104
+ return isGlobalMapFnValid ? getMapFunObj.custom : getMapFunObj.default;
105
+ }
106
+ }