skyeye-svc-common-utils 2.0.0-dev0.2 → 2.0.0-dev0.21

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 (110) hide show
  1. package/dist/index.d.ts +10 -7
  2. package/dist/index.js +10 -7
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/azure/azureKeyVault.d.ts +10 -0
  5. package/dist/lib/azure/azureKeyVault.js +39 -1
  6. package/dist/lib/azure/azureKeyVault.js.map +1 -1
  7. package/dist/lib/azure/cognitiveSearch/QueryType.d.ts +8 -0
  8. package/dist/lib/azure/cognitiveSearch/QueryType.js +13 -0
  9. package/dist/lib/azure/cognitiveSearch/QueryType.js.map +1 -0
  10. package/dist/lib/azure/cognitiveSearch/SearchClient.d.ts +42 -0
  11. package/dist/lib/azure/cognitiveSearch/SearchClient.js +161 -0
  12. package/dist/lib/azure/cognitiveSearch/SearchClient.js.map +1 -0
  13. package/dist/lib/azure/cognitiveSearch/SearchFacets.d.ts +16 -0
  14. package/dist/lib/azure/cognitiveSearch/SearchFacets.js +3 -0
  15. package/dist/lib/azure/cognitiveSearch/SearchFacets.js.map +1 -0
  16. package/dist/lib/azure/cognitiveSearch/SearchFilter.d.ts +68 -0
  17. package/dist/lib/azure/cognitiveSearch/SearchFilter.js +125 -0
  18. package/dist/lib/azure/cognitiveSearch/SearchFilter.js.map +1 -0
  19. package/dist/lib/azure/cognitiveSearch/SearchMode.d.ts +9 -0
  20. package/dist/lib/azure/cognitiveSearch/SearchMode.js +14 -0
  21. package/dist/lib/azure/cognitiveSearch/SearchMode.js.map +1 -0
  22. package/dist/lib/azure/cognitiveSearch/SearchOption.d.ts +88 -0
  23. package/dist/lib/azure/cognitiveSearch/SearchOption.js +104 -0
  24. package/dist/lib/azure/cognitiveSearch/SearchOption.js.map +1 -0
  25. package/dist/lib/azure/cognitiveSearch/SearchOrder.d.ts +9 -0
  26. package/dist/lib/azure/cognitiveSearch/SearchOrder.js +3 -0
  27. package/dist/lib/azure/cognitiveSearch/SearchOrder.js.map +1 -0
  28. package/dist/lib/azure/cognitiveSearch/index.d.ts +7 -0
  29. package/dist/lib/azure/cognitiveSearch/index.js +24 -0
  30. package/dist/lib/azure/cognitiveSearch/index.js.map +1 -0
  31. package/dist/lib/azure/index.d.ts +7 -0
  32. package/dist/lib/azure/index.js +24 -0
  33. package/dist/lib/azure/index.js.map +1 -0
  34. package/dist/lib/fetch/index.d.ts +2 -0
  35. package/dist/lib/fetch/index.js +19 -0
  36. package/dist/lib/fetch/index.js.map +1 -0
  37. package/dist/lib/fetch/sendRequest.d.ts +8 -0
  38. package/dist/lib/fetch/sendRequest.js +25 -0
  39. package/dist/lib/fetch/sendRequest.js.map +1 -0
  40. package/dist/lib/fetch/sendRequestWithTimeout.d.ts +9 -0
  41. package/dist/lib/fetch/sendRequestWithTimeout.js +53 -0
  42. package/dist/lib/fetch/sendRequestWithTimeout.js.map +1 -0
  43. package/dist/lib/httpClient.d.ts +1 -0
  44. package/dist/lib/httpClient.js +18 -14
  45. package/dist/lib/httpClient.js.map +1 -1
  46. package/dist/lib/index.d.ts +3 -0
  47. package/dist/lib/index.js +20 -0
  48. package/dist/lib/index.js.map +1 -0
  49. package/dist/lib/teams/broadcastLogMessage.d.ts +8 -0
  50. package/dist/lib/teams/broadcastLogMessage.js +81 -0
  51. package/dist/lib/teams/broadcastLogMessage.js.map +1 -0
  52. package/dist/lib/teams/index.d.ts +2 -0
  53. package/dist/lib/teams/index.js +19 -0
  54. package/dist/lib/teams/index.js.map +1 -0
  55. package/dist/lib/teams/logMessage.d.ts +51 -0
  56. package/dist/lib/teams/logMessage.js +33 -0
  57. package/dist/lib/teams/logMessage.js.map +1 -0
  58. package/dist/utils/appConst.d.ts +57 -0
  59. package/dist/utils/appConst.js +56 -1
  60. package/dist/utils/appConst.js.map +1 -1
  61. package/dist/utils/baseClass/appDataSource.d.ts +7 -0
  62. package/dist/utils/baseClass/appDataSource.js +31 -0
  63. package/dist/utils/baseClass/appDataSource.js.map +1 -0
  64. package/dist/utils/baseClass/baseController.js +8 -8
  65. package/dist/utils/baseClass/baseController.js.map +1 -1
  66. package/dist/utils/index.d.ts +2 -0
  67. package/dist/utils/index.js +19 -0
  68. package/dist/utils/index.js.map +1 -0
  69. package/dist/utils/logger/morganLogger.d.ts +2 -4
  70. package/dist/utils/logger/morganLogger.js +3 -3
  71. package/dist/utils/logger/morganLogger.js.map +1 -1
  72. package/dist/utils/middleware/loadConfig.d.ts +128 -0
  73. package/dist/utils/middleware/loadConfig.js +143 -0
  74. package/dist/utils/middleware/loadConfig.js.map +1 -0
  75. package/package.json +26 -23
  76. package/src/index.ts +10 -7
  77. package/src/lib/azure/azureKeyVault.ts +44 -0
  78. package/src/lib/azure/cognitiveSearch/QueryType.ts +8 -0
  79. package/src/lib/azure/cognitiveSearch/SearchClient.ts +149 -0
  80. package/src/lib/azure/cognitiveSearch/SearchFacets.ts +16 -0
  81. package/src/lib/azure/cognitiveSearch/SearchFilter.ts +140 -0
  82. package/src/lib/azure/cognitiveSearch/SearchMode.ts +9 -0
  83. package/src/lib/azure/cognitiveSearch/SearchOption.ts +155 -0
  84. package/src/lib/azure/cognitiveSearch/SearchOrder.ts +9 -0
  85. package/src/lib/azure/cognitiveSearch/index.ts +7 -0
  86. package/src/lib/azure/index.ts +7 -0
  87. package/src/lib/fetch/index.ts +2 -0
  88. package/src/lib/fetch/sendRequest.ts +10 -0
  89. package/src/lib/fetch/sendRequestWithTimeout.ts +38 -0
  90. package/src/lib/httpClient.ts +27 -26
  91. package/src/lib/index.ts +3 -0
  92. package/src/lib/teams/broadcastLogMessage.ts +70 -0
  93. package/src/lib/teams/index.ts +2 -0
  94. package/src/lib/teams/logMessage.ts +55 -0
  95. package/src/utils/appConfig.ts +2 -2
  96. package/src/utils/appConst.ts +64 -0
  97. package/src/utils/baseClass/appDataSource.ts +23 -0
  98. package/src/utils/baseClass/baseController.ts +9 -9
  99. package/src/utils/index.ts +2 -0
  100. package/src/utils/logger/morganLogger.ts +2 -2
  101. package/src/utils/middleware/loadConfig.ts +150 -0
  102. package/.env +0 -3
  103. package/dist/utils/baseClass/dataSourceManager.d.ts +0 -11
  104. package/dist/utils/baseClass/dataSourceManager.js +0 -54
  105. package/dist/utils/baseClass/dataSourceManager.js.map +0 -1
  106. package/dist/utils/middleware/startdbConnection.d.ts +0 -1
  107. package/dist/utils/middleware/startdbConnection.js +0 -30
  108. package/dist/utils/middleware/startdbConnection.js.map +0 -1
  109. package/src/utils/baseClass/dataSourceManager.ts +0 -44
  110. package/src/utils/middleware/startdbConnection.ts +0 -20
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @classdesc SearchFilter is the object to construct the Filter when using Azure cognitive search
3
+ * @property {string} attr - the attribute, so called props, to be filtered by the condition
4
+ * @property {SearchFilterCondition} condition - the condition to filter, see enum @SearchFilter for more information
5
+ * @property {any} value - attr should under the condition with value
6
+ * @property {boolean} isCollection - true if the filed is defined as a collection in Azure Cognitive Search
7
+ * @property {'any' | 'all'} collectionOperation - collection search settings
8
+ * @see https://docs.microsoft.com/en-us/azure/search/search-query-understand-collection-filters
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const primaryFilter = new SearchFilter('isPrimary', EQUAL, true);
13
+ * const mentionedCompanyListFilter = new SearchFilter('companyMentioned', CONTAINS, [...]);
14
+ * ```
15
+ */
16
+ export class SearchFilter {
17
+ attr: string;
18
+ condition: SearchFilterCondition;
19
+ value: any;
20
+ isCollection: boolean = false;
21
+ collectionOperation: 'any' | 'all' = 'any';
22
+
23
+ constructor(
24
+ attr: string,
25
+ condition: SearchFilterCondition,
26
+ value: any,
27
+ isCollection: boolean = false,
28
+ collectionOperation: 'any' | 'all' = 'any') {
29
+ this.attr = attr;
30
+ this.condition = condition;
31
+ this.value = value;
32
+ this.isCollection = isCollection;
33
+ this.collectionOperation = collectionOperation;
34
+ }
35
+
36
+ /**
37
+ * @description flatten is to compose the instance into a string when using Search
38
+ * @returns {string} the composite of the instance
39
+ */
40
+ flatten(): string {
41
+ if (this.isCollection) {
42
+ switch (this.condition) {
43
+ case CONTAINS:
44
+ return `${this.attr}/${this.collectionOperation}(i: search.in(i,'${this.value.toString()}', ','))`;
45
+ case EXCLUDE:
46
+ return `${this.attr}/${this.collectionOperation}(i: not search.in(i,'${this.value.toString()}', ','))`;
47
+ default:
48
+ throw new Error(`SearchFilter/flatten: Unknown condition for collection`);
49
+ }
50
+ } else {
51
+ switch (this.condition) {
52
+ case EQUAL:
53
+ case NOT_EQUAL:
54
+ case LESS_THAN_OR_EQUAL:
55
+ case GREATER_THAN_OR_EQUAL:
56
+ return `${this.attr} ${this.condition} ${ typeof(this.value) === 'string' ? `'${this.value}'` : this.value }`;
57
+ case CONTAINS:
58
+ return `search.in(${this.attr}, '${this.value.toString()}', ',')`;
59
+ case EXCLUDE:
60
+ return `${this.condition} search.in(${this.attr}, '${this.value.toString()}', ',')`;
61
+ default:
62
+ throw new Error(`SearchFilter/flatten: Unknown condition`);
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * @description The condition when filtering an params.
70
+ */
71
+ export enum SearchFilterCondition {
72
+ contains = '',
73
+ exclude = 'not',
74
+ equal = 'eq',
75
+ notEqual = 'ne',
76
+ lessThanOrEqual = 'le',
77
+ greaterThanOrEqual = 'ge'
78
+ }
79
+
80
+ /**
81
+ * @description The following const params is a easy-to-use type when implementing a SearchFilter,
82
+ * so that the developers no longer need to call the enum with the class reference
83
+ */
84
+ export const CONTAINS = SearchFilterCondition.contains;
85
+ export const EXCLUDE = SearchFilterCondition.exclude;
86
+ export const EQUAL = SearchFilterCondition.equal;
87
+ export const NOT_EQUAL = SearchFilterCondition.notEqual;
88
+ export const LESS_THAN_OR_EQUAL = SearchFilterCondition.lessThanOrEqual;
89
+ export const GREATER_THAN_OR_EQUAL = SearchFilterCondition.greaterThanOrEqual;
90
+
91
+ /**
92
+ * @description describe types of combination
93
+ */
94
+ export const enum CombinationType {
95
+ and = 'and',
96
+ or = 'or'
97
+ };
98
+
99
+ /**
100
+ * combination is a function ONLY be used for constructing two function: AND and OR,
101
+ * then it would be express into a string.
102
+ * @param {CombinationType} type - to indicate the combination type
103
+ * @param {Array<Array<string | SearchFilter>>} args - the arguments which need to be construct into string
104
+ * @returns {string} aggregation of the given args
105
+ */
106
+ const combination = (type: CombinationType, ...args: Array<string | SearchFilter>): string => {
107
+ if (args.length === 0) { return ''; }
108
+
109
+ function flatten(obj: string | SearchFilter): string {
110
+ return typeof(obj) === 'string' ? obj : obj.flatten();
111
+ }
112
+
113
+ let res: string = !!args[0] ? flatten(args[0]) : '';
114
+
115
+ for (let i = 1; i < args.length; ++i) {
116
+ if (!!args) {
117
+ res += ` ${type} ${flatten(args[i])}`;
118
+ }
119
+ }
120
+
121
+ return `(${res})`;
122
+ }
123
+
124
+ /**
125
+ * @description AND helps developers to compose a nested filter for condition AND.
126
+ * @param {Array<Array<string | SearchFilter>>} args - the arguments which need to be construct into string
127
+ * @returns {string} aggregation of the given args
128
+ */
129
+ export const AND = (...args: Array<string | SearchFilter>): string => {
130
+ return combination(CombinationType.and, ...args);
131
+ }
132
+
133
+ /**
134
+ * @description OR helps developers to compose a nested filter for condition OR.
135
+ * @param {Array<Array<string | SearchFilter>>} args - the arguments which need to be construct into string
136
+ * @returns {string} aggregation of the given args
137
+ */
138
+ export const OR = (...args: Array<string | SearchFilter>): string => {
139
+ return combination(CombinationType.or, ...args);
140
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @description Specifies whether any or all of the search terms must be matched
3
+ * in order to count the document as a match.
4
+ * @see https://docs.microsoft.com/en-us/rest/api/searchservice/search-documents
5
+ */
6
+ export enum SearchMode {
7
+ all = 'all',
8
+ any = 'any'
9
+ }
@@ -0,0 +1,155 @@
1
+ import { AND, OR, CombinationType, SearchFilter } from './SearchFilter';
2
+ import { SearchFacets } from './SearchFacets';
3
+ import { SearchOrder } from './SearchOrder';
4
+ import { SearchMode } from './SearchMode';
5
+ import { QueryType } from './QueryType';
6
+
7
+ /**
8
+ * @description SearchOptionType is the interface to connect with the library
9
+ * @azure/search-documents, it would only be initiated when creating an instance
10
+ * of class @SearchOption, none of the other functions should access this type declaration.
11
+ * @property {Array<string>} select - optional, to indicate the select attributes/props.
12
+ * @property {string | Array<string | SearchFilter>} filter - optional, search with the filter
13
+ * @property {CombinationType} filterCombinationType - optional, combination type of the filters
14
+ * @property {Array<SearchOrder>} orderBy - optional, return order
15
+ * @property {boolean} count - optional, if true, return with the count of the documents.
16
+ * @property {number} top - optional, total volume when retrieving the data
17
+ * @property {number} skip - optional, skip how many document
18
+ * @property {Array<SearchFacets>} facets - optional, retrieve data's facets
19
+ * @property {SearchMode} searchMode - required, different mode for search
20
+ */
21
+ type SearchOptionType = {
22
+ select?: string[],
23
+ filter?: string | Array<string | SearchFilter>,
24
+ filterCombinationType?: CombinationType,
25
+ orderBy?: SearchOrder[],
26
+ count?: boolean,
27
+ top?: number,
28
+ skip?: number,
29
+ facets?: SearchFacets[],
30
+ searchMode?: SearchMode,
31
+ searchFields?: string[],
32
+ scoringProfile?: string,
33
+ highlightFields?: string,
34
+ highlightPreTag?: string,
35
+ highlightPostTag?: string,
36
+ queryType?: QueryType
37
+ }
38
+
39
+ /**
40
+ * @classdesc the class is to wrap an option when retrieving the data from
41
+ * @azure/search-documents/query
42
+ * @property searchOptionType - to store the interface of the option, noted that
43
+ * when calling the query, you should use SearchOption.value() instead of using
44
+ * this attribute.
45
+ */
46
+ export class SearchOption {
47
+ searchOptionType?: SearchOptionType;
48
+
49
+ /**
50
+ * @description when initializing a SearchOption, the defaultSearchOption
51
+ * will override the undefined props
52
+ */
53
+ static defaultSearchOption: SearchOptionType = {
54
+ count: false,
55
+ top: 50,
56
+ skip: 0,
57
+ searchMode: SearchMode.all,
58
+ filterCombinationType: CombinationType.and
59
+ }
60
+
61
+ constructor(searchOptionType?: SearchOptionType) {
62
+ // override the default settings if the value is empty from the given OptionType
63
+ this.searchOptionType = { ...SearchOption.defaultSearchOption, ...searchOptionType };
64
+ }
65
+
66
+ /**
67
+ * @description to compose flatten-filter string for @azure/search-documents
68
+ * @returns {string} flatten-filter string
69
+ */
70
+ private composeFilter(): string {
71
+ if (typeof(this.searchOptionType.filter) === 'string') { return this.searchOptionType.filter; }
72
+ return this.searchOptionType.filterCombinationType === CombinationType.and ?
73
+ AND(...this.searchOptionType.filter)
74
+ :
75
+ OR(...this.searchOptionType.filter);
76
+ }
77
+
78
+ /**
79
+ * @description to compose flatten-order string for @azure/search-documents
80
+ * @returns {Array<string>} flatten-order string
81
+ */
82
+ private composeOrder(): string[] {
83
+ return this.searchOptionType.orderBy.map(
84
+ (s: SearchOrder) => `${s.attr} ${s.orderBy}`
85
+ );
86
+ }
87
+
88
+ /**
89
+ * @description to compose flatten-facets string for @azure/search-documents
90
+ * @returns {Array<string>} flatten-facets string
91
+ */
92
+ private composeFacets(): string[] {
93
+ return this.searchOptionType.facets.map((s: SearchFacets) => {
94
+ let res = `${s.attr}`;
95
+ if (s.count !== null && s.count !== undefined) { res+= `,count:${s.count}`; }
96
+ if (!!s.sort) { res += `,sort:${s.sort}`; }
97
+ if (s.interval !== null && s.interval !== undefined) { res += `,interval:${s.interval}`; }
98
+ if (!!s.timeoffset) { res += `,timeoffset:${s.timeoffset}`; }
99
+ return res;
100
+ });
101
+ }
102
+
103
+ /**
104
+ * @description as an adaptor to transform the type for @azure/search-documents
105
+ * @returns {Object} object which can be accepted by @azure/search-documents
106
+ */
107
+ value = () => {
108
+ let value: {
109
+ select?: string[],
110
+ filter?: string;
111
+ orderBy?: string[],
112
+ includeTotalCount?: boolean,
113
+ top?: number,
114
+ skip?: number,
115
+ facets?: string[],
116
+ searchMode?: string,
117
+ searchFields?: string[],
118
+ scoringProfile?: string,
119
+ highlightFields?: string,
120
+ highlightPreTag?: string,
121
+ highlightPostTag?: string,
122
+ queryType?:string
123
+ } = {};
124
+
125
+ value.includeTotalCount = this.searchOptionType.count;
126
+ value.select = this.searchOptionType.select;
127
+ value.skip = this.searchOptionType.skip;
128
+ value.top = this.searchOptionType.top;
129
+ value.searchMode = this.searchOptionType.searchMode;
130
+ value.searchFields = this.searchOptionType.searchFields;
131
+ value.scoringProfile = this.searchOptionType.scoringProfile;
132
+ value.highlightFields = this.searchOptionType.highlightFields;
133
+ value.highlightPreTag = this.searchOptionType.highlightPreTag;
134
+ value.highlightPostTag = this.searchOptionType.highlightPostTag;
135
+ value.queryType = this.searchOptionType.queryType;
136
+
137
+ if (!!this.searchOptionType.filter) {
138
+ value.filter = this.composeFilter();
139
+ }
140
+
141
+ if (!!this.searchOptionType.select && this.searchOptionType.select.length) {
142
+ value.select = this.searchOptionType.select;
143
+ }
144
+
145
+ if (!!this.searchOptionType.orderBy && this.searchOptionType.orderBy.length) {
146
+ value.orderBy = this.composeOrder();
147
+ }
148
+
149
+ if (!!this.searchOptionType.facets && this.searchOptionType.facets.length) {
150
+ value.facets = this.composeFacets();
151
+ }
152
+
153
+ return value;
154
+ }
155
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @description SearchOrder as a type to depict the orderBy props
3
+ * @property {string} attr - the search field which is used to be sorted
4
+ * @property {'desc' | 'asc'} orderBy - descent or ascent
5
+ */
6
+ export type SearchOrder = {
7
+ attr: string;
8
+ orderBy: 'desc' | 'asc';
9
+ }
@@ -0,0 +1,7 @@
1
+ export * from './QueryType';
2
+ export * from './SearchClient';
3
+ export * from './SearchFacets';
4
+ export * from './SearchFilter';
5
+ export * from './SearchMode';
6
+ export * from './SearchOption';
7
+ export * from './SearchOrder';
@@ -0,0 +1,7 @@
1
+ export * from './cognitiveSearch';
2
+ export * from './azureBlobStorage';
3
+ export * from './azureKeyVault';
4
+ export * from './azureServiceBusClient';
5
+ export * from './azureServiceBusQueue';
6
+ export * from './azureStorageQueue';
7
+ export * from './azureTableStorage';
@@ -0,0 +1,2 @@
1
+ export * from './sendRequest';
2
+ export * from './sendRequestWithTimeout';
@@ -0,0 +1,10 @@
1
+ import fetch from 'node-fetch';
2
+
3
+
4
+ /**
5
+ * @description wrapper of node-fetch
6
+ * @param {string} url - request endpoint
7
+ * @param {any} options - fetch options
8
+ * @returns {any} response
9
+ */
10
+ export const sendRequest = async (url: string, options: any) => await fetch(url, options);
@@ -0,0 +1,38 @@
1
+ import fetch from 'node-fetch';
2
+ import { AbortController } from '@azure/abort-controller';
3
+ import { thirdPartyRegulation } from '../../utils/appConst';
4
+
5
+
6
+ /**
7
+ * @description to store the current processing request, it should only be called
8
+ * by function sendRequestWithTimeout
9
+ */
10
+ const abortedSet: Set<string> = new Set<string>();
11
+
12
+ /**
13
+ * @description send a request which will automatically abort within a given time
14
+ * if the request is not response yet
15
+ * @param {string} url - url of request endpoint
16
+ * @param {any} options - fetch option
17
+ * @param {number} abortWithinMs - automatically abort after given millisecond
18
+ * @returns {any} response
19
+ */
20
+ export async function sendRequestWithTimeout(
21
+ url: string,
22
+ options: any,
23
+ abortWithinMs: number = thirdPartyRegulation.requestLimitationMsec
24
+ ): Promise<any> {
25
+ const controller = new AbortController();
26
+ const timeout = setTimeout(() => {
27
+ abortedSet.add(url);
28
+ controller.abort();
29
+ }, abortWithinMs);
30
+ try {
31
+ const response = await fetch(url, { signal: controller.signal, ...options});
32
+ clearTimeout(timeout);
33
+ return response;
34
+ } catch (err) {
35
+ if (abortedSet.has(url) && (err as Error).name === 'AbortError') { abortedSet.delete(url); }
36
+ throw err;
37
+ }
38
+ }
@@ -1,36 +1,37 @@
1
+ import fetch, { Response, RequestInit } from 'node-fetch';
1
2
  import { UserInfo } from '../interfaces';
2
3
  import { getInternalClient, userInfoSimplifier } from '../utils/commonUtils';
3
4
  import { generateJWT } from '../utils/commonUtils';
4
5
  import {commonAppConst, jsonContentTypeHeaders } from '../utils/appConst';
5
- const fetch = require('node-fetch');
6
+ import { logger } from '../utils/logger/logger';
6
7
 
7
8
  export async function httpRequest(method: string, url : string, data: any = null, headers : any = jsonContentTypeHeaders()): Promise<Response> {
8
- let init = {};
9
- init = data === null ? {
10
- method: method,
11
- cache: "no-cache",
12
- headers: headers
13
- } : {
14
- method: method,
15
- cache: "no-cache",
16
- headers: headers,
17
- body: JSON.stringify(data)
18
- };
19
-
20
- return fetch(url, init);
9
+ const init: RequestInit = {
10
+ method,
11
+ headers,
12
+ body: data === null ? undefined : JSON.stringify(data)
13
+ }
14
+ return fetch(url, init);
21
15
  }
22
16
 
23
17
  export async function sendInternalHttpRequest(userInfo:UserInfo, client: string, endpoint: string, data: any = null, method?: string, select?:string[]) {
24
- method = method ?? commonAppConst.httpMethod.POST;
25
- const internalClient = await getInternalClient(client)
26
- const url = internalClient + endpoint;
27
- let headers:any = jsonContentTypeHeaders();
28
- if(userInfo){
29
- userInfo = await userInfoSimplifier(userInfo, select)
30
- let token: string = `${commonAppConst.httpHeader.BEARER} ${await generateJWT(userInfo)}`;
31
- headers[commonAppConst.httpHeader.AUTHORIZATION] = token;
32
- }
33
- return httpRequest(method, url, data, headers).then(response => {
34
- return response.json()
35
- })
18
+ method = method ?? commonAppConst.httpMethod.POST;
19
+ const internalClient = await getInternalClient(client)
20
+ const url = internalClient + endpoint;
21
+ let headers:any = jsonContentTypeHeaders();
22
+ if(userInfo){
23
+ userInfo = await userInfoSimplifier(userInfo, select)
24
+ let token: string = `${commonAppConst.httpHeader.BEARER} ${await generateJWT(userInfo)}`;
25
+ headers[commonAppConst.httpHeader.AUTHORIZATION] = token;
26
+ }
27
+
28
+ let response: Response;
29
+ try {
30
+ response = await httpRequest(method, url, data, headers);
31
+ return response.json();
32
+ }
33
+ catch (err) {
34
+ logger.error(`Error while sending internal http request, receiving response: ${response}`);
35
+ throw new Error(`${err}`);
36
+ }
36
37
  }
@@ -0,0 +1,3 @@
1
+ export * from './azure';
2
+ export * from './fetch';
3
+ export * from './teams';
@@ -0,0 +1,70 @@
1
+ import { LogLevel, LogMessage } from './logMessage';
2
+ import { appConfig, loadConfig } from '../../utils/middleware/loadConfig';
3
+ import { sendRequestWithTimeout } from '../fetch';
4
+
5
+
6
+ /**
7
+ * @description broadcast log message to specific Microsoft Teams channels
8
+ * @param {LogLevel} level - level of the log
9
+ * @param {string} message - log message
10
+ * @returns {void} the function is deposable, thus it won't return any of information
11
+ */
12
+ export async function broadcastLogMessage(level: LogLevel, message: string) {
13
+ // we only broadcast two levels of log.
14
+ if (level !== 'error' && level !== 'warn') { return; }
15
+
16
+ /**
17
+ * When the keyVaultName is empty, loadConfig() should be called in order to fetch the value as possible
18
+ * since we are not using lazy-loading here. It would be better to transform into a lazy-loading mechanism.
19
+ */
20
+ if (appConfig === undefined) {
21
+ try {
22
+ await loadConfig();
23
+ } catch (err) {
24
+ throw err;
25
+ }
26
+ }
27
+
28
+ const time = new Date().toISOString();
29
+ const logMessage: LogMessage = {
30
+ level,
31
+ message,
32
+ time,
33
+ environment: appConfig.environment,
34
+ appName: appConfig.appName,
35
+ version: appConfig.version
36
+ };
37
+
38
+ const messageCard: Object = ((): Object => {
39
+ return {
40
+ '@type': 'MessageCard',
41
+ '@context': 'http://schema.org/extensions',
42
+ 'summary': 'Teams broadcasting of Logs',
43
+ 'sections': [{
44
+ 'activityTitle': `# ***Service Error Log - ${logMessage.level}***`,
45
+ 'facts': [
46
+ { 'name': 'Time', 'value': logMessage.time },
47
+ { 'name': 'Environment', 'value': logMessage.environment },
48
+ { 'name': 'Service', 'value': logMessage.appName },
49
+ { 'name': 'Version', 'value': logMessage.version },
50
+ { 'name': 'Message', 'value': logMessage.message }
51
+ ],
52
+ 'markdown': true
53
+ }],
54
+ 'potentialAction': [
55
+ {
56
+ '@type': 'OpenUri',
57
+ 'name': 'View Documentation',
58
+ 'targets': [ { 'os': 'default', 'uri': '' } ]
59
+ }
60
+ ]
61
+ }
62
+ })();
63
+
64
+ const url = level === 'error' ?
65
+ appConfig.telemetry.teamsChannelWebHook.errorChannel
66
+ :
67
+ appConfig.telemetry.teamsChannelWebHook.warnChannel;
68
+
69
+ await sendRequestWithTimeout(url, { method: 'POST', body: JSON.stringify(messageCard) });
70
+ }
@@ -0,0 +1,2 @@
1
+ export * from './broadcastLogMessage';
2
+ export * from './logMessage';
@@ -0,0 +1,55 @@
1
+ import { Environment } from '../../utils';
2
+ import { z } from 'zod';
3
+
4
+
5
+ /**
6
+ * @description we classified the logs into 4 levels and one supporting level:
7
+ * - `verbose`: The most fine-grained information only used in rare cases where
8
+ * you need the full visibility of what is happening in your application
9
+ * and inside the third-party libraries that you use
10
+ * - `info`: The standard log level indicating that something happened;
11
+ * the application entered a certain state. It should be purely informative and
12
+ * not looking into them on a regular basis
13
+ * - `warn`: the log level that indicates that something unexpected happened in
14
+ * the application, a problem, or a situation that might disturb one of the
15
+ * processes. But that doesn’t mean that the application failed
16
+ * - `error`: error, states as `FATAL` in the modern log leveling to alleviate DRY.
17
+ * It tells that the application encountered an event or entered a state in which
18
+ * one of the crucial business functionality is no longer working
19
+ * - `disabled`: supporting enum for disabling the functionality of logger
20
+ *
21
+ * @example
22
+ * ```markdown
23
+ * - verbose: annotate each step in the algorithm or each individual query
24
+ * with parameters in your code
25
+ * - info: request of authorization API which states whether a user sign-on success or not
26
+ * - warn`: a parsing error occurred that resulted in a certain document not being processed
27
+ * - error: when the application is not able to connect to a crucial data store like a database
28
+ * or one of the sub-service is unavailable
29
+ * ```
30
+ * @see https://sematext.com/blog/logging-levels/
31
+ */
32
+ export const zLogLevel = z.enum(['verbose', 'info', 'warn', 'error', 'disabled']);
33
+
34
+ /**
35
+ * @description typedef of `zLogLevel`
36
+ */
37
+ export type LogLevel = z.infer<typeof zLogLevel>;
38
+
39
+ /**
40
+ * @description definition of the context that a log should contains
41
+ * @property {LogLevel} level - log level
42
+ * @property {string} time - when does the log be generated
43
+ * @property {string} appName - the service which generates the logs
44
+ * @property {Environment} environment - log happened in which environment
45
+ * @property {string} message - log message
46
+ * @property {string} version - current app version
47
+ */
48
+ export type LogMessage = {
49
+ level: LogLevel;
50
+ time: string;
51
+ appName: string;
52
+ environment: Environment;
53
+ message: string;
54
+ version: string;
55
+ }
@@ -1,5 +1,5 @@
1
1
  import dotenv from 'dotenv';
2
- import { commonAppConst } from './appConst';
2
+
3
3
  if (process.env.NODE_ENV !== undefined) {
4
4
  dotenv.config({ path: `${process.env.NODE_ENV}.env` });
5
5
  }
@@ -49,4 +49,4 @@ export const commonAppConfig = {
49
49
  DailyDigest: process.env.DAILYDIGEST_CLIENT,
50
50
  Announcement: process.env.ANNOUNCEMENT_CLIENT,
51
51
  APIM: process.env.APIM_CLIENT,
52
- }
52
+ }
@@ -1,3 +1,5 @@
1
+ import { z } from 'zod';
2
+
1
3
  export const commonAppConst = {
2
4
  emptyGuid: '00000000-0000-0000-0000-000000000000',
3
5
  sampleStatus: {
@@ -155,3 +157,65 @@ export const exitErrorMessage:Record<string, string> = {
155
157
  SQLQueryRunner: `Requests can only be made in the LoggedIn state, not the SentClientRequest state`,
156
158
  UnknownTimeout: `operation timed out for an unknown reason`
157
159
  }
160
+
161
+ /**
162
+ * @description environment cuts some of the functionalities, including log levels
163
+ * and local-env file location, to optimize our services.
164
+ * @enum {string}
165
+ */
166
+ export const zEnvironment = z.enum(['test', 'dev', 'development', 'preview', 'staging', 'beta', 'production']);
167
+
168
+ /**
169
+ * @description typedef of `zEnvironment`
170
+ */
171
+ export type Environment = z.infer<typeof zEnvironment>;
172
+
173
+ /**
174
+ * @description const to regulate, control the third parties' usage
175
+ * @property {number} requestLimitationMsec - default request abort time
176
+ * @property {number} requestDefaultRetry - default retry times if the request is failed
177
+ */
178
+ export const thirdPartyRegulation = {
179
+ requestLimitationMsec: 5000,
180
+ requestDefaultRetry: 2
181
+ };
182
+
183
+ /**
184
+ * @description settings for logger
185
+ * @property {string} path - local storage path of log files
186
+ * @property {boolean} teamsChannelDisabled - when the log level is `warn`/`error`,
187
+ * the messages will be delivered to the Teams channel
188
+ */
189
+ export const loggerSettings = {
190
+ path: './log/',
191
+ teamsChannelDisabled: true
192
+ };
193
+
194
+ /**
195
+ * @description Index of Azure Cognitive Search
196
+ * @enum {string}
197
+ */
198
+ export enum AzureSearchIndex {
199
+ news = 'news',
200
+ company = 'company',
201
+ market = 'market',
202
+ financial = 'financial',
203
+ ott = 'ott',
204
+ nerl = 'nerl',
205
+ people = 'people',
206
+ relationship = 'relationship'
207
+ };
208
+
209
+ /**
210
+ * @description configuration of Azure Cognitive Search
211
+ * @property {number} queryRetryDefault - default retry times for query
212
+ * @property {number} retryIntervalSecond - retry after waiting given second
213
+ * @property {string} credentialSecretName - credential secret name saved in Azure Key Vault
214
+ * @property {string} indexSecretName - index secret name saved in Azure Key Vault
215
+ */
216
+ export const AzureSearchConfig = {
217
+ queryRetryDefault: 3,
218
+ retryIntervalSecond: 1,
219
+ credentialSecretName: 'SkyEye-SVC-Search-Credential',
220
+ indexSecretName: 'SkyEye-SVC-Search-Index'
221
+ };