ObjectUtils.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import configLoader from "./Config";
  15. export type FileContent = {
  16. name: string;
  17. content: string;
  18. };
  19. class ObjectUtils {
  20. static notEmpty<T>(value: T | null | undefined): value is T {
  21. return value != null;
  22. }
  23. static flatten(
  24. object: any,
  25. appendParentPath?: boolean,
  26. parent?: string
  27. ): any {
  28. let result: any = {};
  29. Object.keys(object).forEach(k => {
  30. if (typeof object[k] === "object" && !Array.isArray(object[k])) {
  31. if (object[k]) {
  32. result = {
  33. ...result,
  34. ...this.flatten(object[k], appendParentPath, k),
  35. };
  36. }
  37. } else {
  38. let key = k;
  39. if (appendParentPath && parent) {
  40. key = `${parent}/${k}`;
  41. }
  42. result[key] = object[k];
  43. }
  44. });
  45. if (Object.keys(result).length === 0) {
  46. return null;
  47. }
  48. return result;
  49. }
  50. static skipFields(object: any, fieldNames: string[]) {
  51. const result: any = {};
  52. if (Object.keys(object).length === 0) {
  53. return null;
  54. }
  55. Object.keys(object).forEach(k => {
  56. if (!fieldNames.find(fn => fn === k)) {
  57. result[k] = object[k];
  58. }
  59. });
  60. if (Object.keys(result).length === 0) {
  61. return null;
  62. }
  63. return result;
  64. }
  65. static async wait(ms: number) {
  66. return new Promise<void>(r => {
  67. setTimeout(() => r(), ms);
  68. });
  69. }
  70. static async waitFor(
  71. predicate: () => boolean,
  72. options?: {
  73. timeoutMs?: number;
  74. intervalMs?: number;
  75. silent?: boolean;
  76. }
  77. ) {
  78. const { timeoutMs = 15000, intervalMs = 1000, silent } = options || {};
  79. const startTime = new Date().getTime();
  80. const testLoop = async () => {
  81. if (predicate()) {
  82. return;
  83. }
  84. if (new Date().getTime() - startTime > timeoutMs) {
  85. if (!silent) {
  86. throw new Error(`Timeout: waiting for more than ${timeoutMs} ms`);
  87. }
  88. console.log(`Timeout: waiting for more than ${timeoutMs} ms`);
  89. return;
  90. }
  91. await this.wait(intervalMs);
  92. await testLoop();
  93. };
  94. await testLoop();
  95. }
  96. static trim(fieldName: string, value: any): any {
  97. const isPassword =
  98. configLoader.config.passwordFields.find(p => p === fieldName) ||
  99. fieldName.toLowerCase().indexOf("password") > -1;
  100. return typeof value === "string" && !isPassword ? value.trim() : value;
  101. }
  102. static capitalizeFirstLetter(value: string): string {
  103. return value.charAt(0).toUpperCase() + value.slice(1);
  104. }
  105. static async retry(
  106. retryFunction: () => Promise<any>,
  107. retryEvery = 1000,
  108. retryCount = 3
  109. ): Promise<any> {
  110. let currentTry = 0;
  111. const retryLoop = async (): Promise<any> => {
  112. try {
  113. currentTry += 1;
  114. if (currentTry > 1) {
  115. console.log("Retrying... ", currentTry);
  116. }
  117. const result = await retryFunction();
  118. return result;
  119. } catch (err) {
  120. if (currentTry >= retryCount) {
  121. console.error(`Retry failed after ${retryCount} attempts.`);
  122. throw err;
  123. }
  124. await this.wait(retryEvery);
  125. return retryLoop();
  126. }
  127. };
  128. return retryLoop();
  129. }
  130. static isObject(item: any) {
  131. return item && typeof item === "object" && !Array.isArray(item);
  132. }
  133. static mergeDeep(target: any, ...sources: any[]): any {
  134. if (!sources.length) {
  135. return target;
  136. }
  137. const source = sources.shift();
  138. if (this.isObject(target) && this.isObject(source)) {
  139. Object.keys(source).forEach(key => {
  140. if (this.isObject(source[key])) {
  141. if (!target[key]) {
  142. Object.assign(target, { [key]: {} });
  143. }
  144. this.mergeDeep(target[key], source[key]);
  145. } else {
  146. Object.assign(target, { [key]: source[key] });
  147. }
  148. });
  149. }
  150. return this.mergeDeep(target, ...sources);
  151. }
  152. static clone(obj: any) {
  153. return JSON.parse(JSON.stringify(obj));
  154. }
  155. }
  156. export default ObjectUtils;