Remove describe.only
from spec file as this was stopping all other
spec files from running.
Closes #7284
1 |
import {ObjectLiteral} from "../common/ObjectLiteral"; |
|
2 |
|
|
3 | 10 |
export class OrmUtils { |
4 |
|
|
5 |
// -------------------------------------------------------------------------
|
|
6 |
// Public methods
|
|
7 |
// -------------------------------------------------------------------------
|
|
8 |
|
|
9 |
/**
|
|
10 |
* Chunks array into peaces.
|
|
11 |
*/
|
|
12 | 10 |
static chunk<T>(array: T[], size: number): T[][] { |
13 | 6 |
return Array.from(Array(Math.ceil(array.length / size)), (_, i) => { |
14 | 6 |
return array.slice(i * size, i * size + size); |
15 |
});
|
|
16 |
}
|
|
17 |
|
|
18 | 10 |
static splitClassesAndStrings<T>(clsesAndStrings: (string | T)[]): [T[], string[]] { |
19 | 10 |
return [ |
20 | 10 |
(clsesAndStrings).filter((cls): cls is T => typeof cls !== "string"), |
21 | 10 |
(clsesAndStrings).filter((str): str is string => typeof str === "string"), |
22 |
];
|
|
23 |
}
|
|
24 |
|
|
25 | 10 |
static groupBy<T, R>(array: T[], propertyCallback: (item: T) => R): { id: R, items: T[] }[] { |
26 |
return array.reduce((groupedArray, value) => { |
|
27 |
const key = propertyCallback(value); |
|
28 |
let grouped = groupedArray.find(i => i.id === key); |
|
29 |
if (!grouped) { |
|
30 |
grouped = { id: key, items: [] }; |
|
31 |
groupedArray.push(grouped); |
|
32 |
}
|
|
33 |
grouped.items.push(value); |
|
34 |
return groupedArray; |
|
35 |
}, [] as Array<{ id: R, items: T[] }>); |
|
36 |
}
|
|
37 |
|
|
38 |
static uniq<T>(array: T[], criteria?: (item: T) => any): T[]; |
|
39 |
static uniq<T, K extends keyof T>(array: T[], property: K): T[]; |
|
40 | 10 |
static uniq<T, K extends keyof T>(array: T[], criteriaOrProperty?: ((item: T) => any) | K): T[] { |
41 | 10 |
return array.reduce((uniqueArray, item) => { |
42 | 10 |
let found: boolean = false; |
43 | 10 |
if (criteriaOrProperty instanceof Function) { |
44 | 10 |
const itemValue = criteriaOrProperty(item); |
45 | 10 |
found = !!uniqueArray.find(uniqueItem => criteriaOrProperty(uniqueItem) === itemValue); |
46 |
|
|
47 |
} else if (typeof criteriaOrProperty === "string") { |
|
48 |
found = !!uniqueArray.find(uniqueItem => uniqueItem[criteriaOrProperty] === item[criteriaOrProperty]); |
|
49 |
|
|
50 |
} else { |
|
51 |
found = uniqueArray.indexOf(item) !== -1; |
|
52 |
}
|
|
53 |
|
|
54 | 10 |
if (!found) |
55 | 10 |
uniqueArray.push(item); |
56 |
|
|
57 | 10 |
return uniqueArray; |
58 |
}, [] as T[]); |
|
59 |
}
|
|
60 |
|
|
61 | 10 |
static isObject(item: any) { |
62 | 10 |
return (item && typeof item === "object" && !Array.isArray(item)); |
63 |
}
|
|
64 |
|
|
65 |
/**
|
|
66 |
* Deep Object.assign.
|
|
67 |
*
|
|
68 |
* @see http://stackoverflow.com/a/34749873
|
|
69 |
*/
|
|
70 | 10 |
static mergeDeep(target: any, ...sources: any[]): any { |
71 | 10 |
if (!sources.length) return target; |
72 | 10 |
const source = sources.shift(); |
73 |
|
|
74 | 10 |
if (this.isObject(target) && this.isObject(source)) { |
75 | 10 |
for (const key in source) { |
76 | 10 |
const value = source[key]; |
77 | 10 |
if (key === "__proto__" || value instanceof Promise) |
78 |
continue; |
|
79 |
|
|
80 | 10 |
if (this.isObject(value) |
81 | 10 |
&& !(value instanceof Map) |
82 | 10 |
&& !(value instanceof Set) |
83 | 10 |
&& !(value instanceof Date) |
84 | 10 |
&& !(value instanceof Buffer) |
85 | 10 |
&& !(value instanceof RegExp)) { |
86 | 10 |
if (!target[key]) |
87 | 10 |
Object.assign(target, { [key]: Object.create(Object.getPrototypeOf(value)) }); |
88 | 10 |
this.mergeDeep(target[key], value); |
89 |
} else { |
|
90 | 10 |
Object.assign(target, { [key]: value }); |
91 |
}
|
|
92 |
}
|
|
93 |
}
|
|
94 |
|
|
95 | 10 |
return this.mergeDeep(target, ...sources); |
96 |
}
|
|
97 |
|
|
98 |
/**
|
|
99 |
* Deep compare objects.
|
|
100 |
*
|
|
101 |
* @see http://stackoverflow.com/a/1144249
|
|
102 |
*/
|
|
103 | 10 |
static deepCompare(...args: any[]): boolean { |
104 |
let i: any, l: any, leftChain: any, rightChain: any; |
|
105 |
|
|
106 | 10 |
if (arguments.length < 1) { |
107 |
return true; // Die silently? Don't know how to handle such case, please help... |
|
108 |
// throw "Need two or more arguments to compare";
|
|
109 |
}
|
|
110 |
|
|
111 | 10 |
for (i = 1, l = arguments.length; i < l; i++) { |
112 |
|
|
113 | 10 |
leftChain = []; // Todo: this can be cached |
114 | 10 |
rightChain = []; |
115 |
|
|
116 | 10 |
if (!this.compare2Objects(leftChain, rightChain, arguments[0], arguments[i])) { |
117 | 10 |
return false; |
118 |
}
|
|
119 |
}
|
|
120 |
|
|
121 | 10 |
return true; |
122 |
}
|
|
123 |
|
|
124 |
/**
|
|
125 |
* Check if two entity-id-maps are the same
|
|
126 |
*/
|
|
127 | 10 |
static compareIds(firstId: ObjectLiteral|undefined, secondId: ObjectLiteral|undefined): boolean { |
128 | 10 |
if (firstId === undefined || firstId === null || secondId === undefined || secondId === null) |
129 | 10 |
return false; |
130 |
|
|
131 |
// Optimized version for the common case
|
|
132 | 10 |
if ( |
133 | 10 |
((typeof firstId.id === "string" && typeof secondId.id === "string") || |
134 | 10 |
(typeof firstId.id === "number" && typeof secondId.id === "number")) && |
135 | 10 |
Object.keys(firstId).length === 1 && |
136 | 10 |
Object.keys(secondId).length === 1 |
137 |
) { |
|
138 | 10 |
return firstId.id === secondId.id; |
139 |
}
|
|
140 |
|
|
141 | 10 |
return OrmUtils.deepCompare(firstId, secondId); |
142 |
}
|
|
143 |
|
|
144 |
/**
|
|
145 |
* Transforms given value into boolean value.
|
|
146 |
*/
|
|
147 | 10 |
static toBoolean(value: any): boolean { |
148 | 10 |
if (typeof value === "boolean") |
149 |
return value; |
|
150 |
|
|
151 | 10 |
if (typeof value === "string") |
152 |
return value === "true" || value === "1"; |
|
153 |
|
|
154 | 10 |
if (typeof value === "number") |
155 |
return value > 0; |
|
156 |
|
|
157 | 10 |
return false; |
158 |
}
|
|
159 |
|
|
160 |
/**
|
|
161 |
* Composes an object from the given array of keys and values.
|
|
162 |
*/
|
|
163 | 10 |
static zipObject(keys: any[], values: any[]): ObjectLiteral { |
164 |
return keys.reduce((object, column, index) => { |
|
165 |
object[column] = values[index]; |
|
166 |
return object; |
|
167 |
}, {} as ObjectLiteral); |
|
168 |
}
|
|
169 |
|
|
170 |
/**
|
|
171 |
* Compares two arrays.
|
|
172 |
*/
|
|
173 | 10 |
static isArraysEqual(arr1: any[], arr2: any[]): boolean { |
174 | 6 |
if (arr1.length !== arr2.length) return false; |
175 | 6 |
return arr1.every(element => { |
176 | 6 |
return arr2.indexOf(element) !== -1; |
177 |
});
|
|
178 |
}
|
|
179 |
|
|
180 |
// -------------------------------------------------------------------------
|
|
181 |
// Private methods
|
|
182 |
// -------------------------------------------------------------------------
|
|
183 |
|
|
184 | 10 |
private static compare2Objects(leftChain: any, rightChain: any, x: any, y: any) { |
185 |
let p; |
|
186 |
|
|
187 |
// remember that NaN === NaN returns false
|
|
188 |
// and isNaN(undefined) returns true
|
|
189 | 10 |
if (Number.isNaN(x) && Number.isNaN(y)) |
190 |
return true; |
|
191 |
|
|
192 |
// Compare primitives and functions.
|
|
193 |
// Check if both arguments link to the same object.
|
|
194 |
// Especially useful on the step where we compare prototypes
|
|
195 | 10 |
if (x === y) |
196 | 10 |
return true; |
197 |
|
|
198 |
// Unequal, but either is null or undefined (use case: jsonb comparasion)
|
|
199 |
// PR #3776, todo: add tests
|
|
200 | 10 |
if (x === null || y === null || x === undefined || y === undefined) |
201 |
return false; |
|
202 |
|
|
203 |
// Fix the buffer compare bug.
|
|
204 |
// See: https://github.com/typeorm/typeorm/issues/3654
|
|
205 | 10 |
if ((typeof x.equals === "function" || x.equals instanceof Function) && x.equals(y)) |
206 | 6 |
return true; |
207 |
|
|
208 |
// Works in case when functions are created in constructor.
|
|
209 |
// Comparing dates is a common scenario. Another built-ins?
|
|
210 |
// We can even handle functions passed across iframes
|
|
211 | 10 |
if ((typeof x === "function" && typeof y === "function") || |
212 | 10 |
(x instanceof Date && y instanceof Date) || |
213 | 10 |
(x instanceof RegExp && y instanceof RegExp) || |
214 | 10 |
(x instanceof String && y instanceof String) || |
215 | 10 |
(x instanceof Number && y instanceof Number)) |
216 |
return x.toString() === y.toString(); |
|
217 |
|
|
218 |
// At last checking prototypes as good as we can
|
|
219 | 10 |
if (!(x instanceof Object && y instanceof Object)) |
220 | 10 |
return false; |
221 |
|
|
222 | 10 |
if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) |
223 |
return false; |
|
224 |
|
|
225 | 10 |
if (x.constructor !== y.constructor) |
226 |
return false; |
|
227 |
|
|
228 | 10 |
if (x.prototype !== y.prototype) |
229 |
return false; |
|
230 |
|
|
231 |
// Check for infinitive linking loops
|
|
232 | 10 |
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) |
233 |
return false; |
|
234 |
|
|
235 |
// Quick checking of one object being a subset of another.
|
|
236 |
// todo: cache the structure of arguments[0] for performance
|
|
237 | 10 |
for (p in y) { |
238 | 10 |
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { |
239 | 6 |
return false; |
240 |
}
|
|
241 | 10 |
else if (typeof y[p] !== typeof x[p]) { |
242 | 10 |
return false; |
243 |
}
|
|
244 |
}
|
|
245 |
|
|
246 | 10 |
for (p in x) { |
247 | 10 |
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { |
248 |
return false; |
|
249 |
}
|
|
250 | 10 |
else if (typeof y[p] !== typeof x[p]) { |
251 |
return false; |
|
252 |
}
|
|
253 |
|
|
254 | 10 |
switch (typeof (x[p])) { |
255 | 10 |
case "object": |
256 | 10 |
case "function": |
257 |
|
|
258 | 10 |
leftChain.push(x); |
259 | 10 |
rightChain.push(y); |
260 |
|
|
261 | 10 |
if (!this.compare2Objects(leftChain, rightChain, x[p], y[p])) { |
262 |
return false; |
|
263 |
}
|
|
264 |
|
|
265 | 10 |
leftChain.pop(); |
266 | 10 |
rightChain.pop(); |
267 | 10 |
break; |
268 |
|
|
269 | 10 |
default: |
270 | 10 |
if (x[p] !== y[p]) { |
271 | 10 |
return false; |
272 |
}
|
|
273 | 10 |
break; |
274 |
}
|
|
275 |
}
|
|
276 |
|
|
277 | 10 |
return true; |
278 |
}
|
|
279 |
|
|
280 | 10 |
}
|
Read our documentation on viewing source code .