From 7f8b8716ba69e24800e26d6c072c3076c01bfccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leyla=20J=C3=A4hnig?= Date: Mon, 29 Aug 2022 13:10:45 +0200 Subject: refactor(unique): move to helpers (#1298) --- src/modules/helpers/index.ts | 44 ++++++++++++ src/modules/helpers/unique.ts | 158 ++++++++++++++++++++++++++++++++++++++++++ src/modules/unique/index.ts | 26 ++++--- src/modules/unique/unique.ts | 158 ------------------------------------------ 4 files changed, 218 insertions(+), 168 deletions(-) create mode 100644 src/modules/helpers/unique.ts delete mode 100644 src/modules/unique/unique.ts (limited to 'src/modules') diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index fac7e3e2..5228c605 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -2,6 +2,8 @@ import type { Faker } from '../..'; import { FakerError } from '../../errors/faker-error'; import { deprecated } from '../../internal/deprecated'; import { luhnCheckValue } from './luhn-check'; +import type { RecordKey } from './unique'; +import * as uniqueExec from './unique'; /** * Module with various helper methods that transform the method input rather than returning values from locales. @@ -590,4 +592,46 @@ export class Helpers { // return the response recursively until we are done finding all tags return this.fake(res); } + + /** + * Generates a unique result using the results of the given method. + * Used unique entries will be stored internally and filtered from subsequent calls. + * + * @template Method The type of the method to execute. + * @param method The method used to generate the values. + * @param args The arguments used to call the method. + * @param options The optional options used to configure this method. + * @param options.startTime This parameter does nothing. + * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`. + * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`. + * @param options.currentIterations This parameter does nothing. + * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. + * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. + * @param options.store The store of unique entries. Defaults to a global store. + * + * @example + * faker.helpers.unique(faker.name.firstName) // 'Corbin' + */ + unique RecordKey>( + method: Method, + args?: Parameters, + options: { + startTime?: number; + maxTime?: number; + maxRetries?: number; + currentIterations?: number; + exclude?: RecordKey | RecordKey[]; + compare?: (obj: Record, key: RecordKey) => 0 | -1; + store?: Record; + } = {} + ): ReturnType { + const { maxTime = 50, maxRetries = 50 } = options; + return uniqueExec.exec(method, args, { + ...options, + startTime: new Date().getTime(), + maxTime, + maxRetries, + currentIterations: 0, + }); + } } diff --git a/src/modules/helpers/unique.ts b/src/modules/helpers/unique.ts new file mode 100644 index 00000000..31fddec8 --- /dev/null +++ b/src/modules/helpers/unique.ts @@ -0,0 +1,158 @@ +import { FakerError } from '../../errors/faker-error'; + +export type RecordKey = string | number | symbol; + +/** + * Global store of unique values. + * This means that faker should *never* return duplicate values across all API methods when using `Faker.helpers.unique` without passing `options.store`. + */ +const GLOBAL_UNIQUE_STORE: Record = {}; + +/** + * Global exclude list of results. + * Defaults to nothing excluded. + */ +const GLOBAL_UNIQUE_EXCLUDE: RecordKey[] = []; + +/** + * Uniqueness compare function. + * Default behavior is to check value as key against object hash. + * + * @param obj The object to check. + * @param key The key to check. + */ +function defaultCompare( + obj: Record, + key: RecordKey +): 0 | -1 { + if (obj[key] === undefined) { + return -1; + } + return 0; +} + +/** + * Logs the given code as an error and throws it. + * Also logs a message for helping the user. + * + * @param startTime The time the execution started. + * @param now The current time. + * @param code The error code. + * @param store The store of unique entries. + * @param currentIterations Current iteration or retries of `unique.exec` (current loop depth). + * + * @throws The given error code with additional text. + */ +function errorMessage( + startTime: number, + now: number, + code: string, + store: Record, + currentIterations: number +): never { + console.error('Error', code); + console.log( + `Found ${Object.keys(store).length} unique entries before throwing error. +retried: ${currentIterations} +total time: ${now - startTime}ms` + ); + throw new FakerError( + `${code} for uniqueness check. + +May not be able to generate any more unique values with current settings. +Try adjusting maxTime or maxRetries parameters for faker.helpers.unique().` + ); +} + +/** + * Generates a unique result using the results of the given method. + * Used unique entries will be stored internally and filtered from subsequent calls. + * + * @template Method The type of the method to execute. + * @param method The method used to generate the values. + * @param args The arguments used to call the method. + * @param options The optional options used to configure this method. + * @param options.startTime The time this execution stared. Defaults to `new Date().getTime()`. + * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`. + * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`. + * @param options.currentIterations The current attempt. Defaults to `0`. + * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. + * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. + * @param options.store The store of unique entries. Defaults to `GLOBAL_UNIQUE_STORE`. + */ +export function exec RecordKey>( + method: Method, + args: Parameters, + options: { + startTime?: number; + maxTime?: number; + maxRetries?: number; + currentIterations?: number; + exclude?: RecordKey | RecordKey[]; + compare?: (obj: Record, key: RecordKey) => 0 | -1; + store?: Record; + } = {} +): ReturnType { + const now = new Date().getTime(); + + const { + startTime = new Date().getTime(), + maxTime = 50, + maxRetries = 50, + compare = defaultCompare, + store = GLOBAL_UNIQUE_STORE, + } = options; + let { exclude = GLOBAL_UNIQUE_EXCLUDE } = options; + options.currentIterations = options.currentIterations ?? 0; + + // Support single exclude argument as string + if (!Array.isArray(exclude)) { + exclude = [exclude]; + } + + // if (options.currentIterations > 0) { + // console.log('iterating', options.currentIterations) + // } + + // console.log(now - startTime) + if (now - startTime >= maxTime) { + return errorMessage( + startTime, + now, + `Exceeded maxTime: ${maxTime}`, + store, + options.currentIterations + ); + } + + if (options.currentIterations >= maxRetries) { + return errorMessage( + startTime, + now, + `Exceeded maxRetries: ${maxRetries}`, + store, + options.currentIterations + ); + } + + // Execute the provided method to find a potential satisfied value. + const result: ReturnType = method.apply(this, args); + + // If the result has not been previously found, add it to the found array and return the value as it's unique. + if (compare(store, result) === -1 && exclude.indexOf(result) === -1) { + store[result] = result; + options.currentIterations = 0; + return result; + } else { + // console.log('conflict', result); + options.currentIterations++; + return exec(method, args, { + ...options, + startTime, + maxTime, + maxRetries, + compare, + exclude, + }); + } +} diff --git a/src/modules/unique/index.ts b/src/modules/unique/index.ts index d77004e4..6c02d6d8 100644 --- a/src/modules/unique/index.ts +++ b/src/modules/unique/index.ts @@ -1,11 +1,14 @@ -import type { RecordKey } from './unique'; -import * as uniqueExec from './unique'; +import type { Faker } from '../..'; +import { deprecated } from '../../internal/deprecated'; +import type { RecordKey } from '../helpers/unique'; /** * Module to generate unique entries. + * + * @deprecated */ export class Unique { - constructor() { + constructor(private readonly faker: Faker) { // Bind `this` so namespaced is working correctly for (const name of Object.getOwnPropertyNames(Unique.prototype)) { if ( @@ -36,8 +39,12 @@ export class Unique { * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. * @param options.store The store of unique entries. Defaults to a global store. * + * @see faker.helpers.unique() + * * @example * faker.unique(faker.name.firstName) // 'Corbin' + * + * @deprecated Use faker.helpers.unique() instead. */ unique RecordKey>( method: Method, @@ -52,13 +59,12 @@ export class Unique { store?: Record; } = {} ): ReturnType { - const { maxTime = 50, maxRetries = 50 } = options; - return uniqueExec.exec(method, args, { - ...options, - startTime: new Date().getTime(), - maxTime, - maxRetries, - currentIterations: 0, + deprecated({ + deprecated: 'faker.unique()', + proposed: 'faker.helpers.unique()', + since: '7.5', + until: '8.0', }); + return this.faker.helpers.unique(method, args, options); } } diff --git a/src/modules/unique/unique.ts b/src/modules/unique/unique.ts deleted file mode 100644 index 70408faf..00000000 --- a/src/modules/unique/unique.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { FakerError } from '../../errors/faker-error'; - -export type RecordKey = string | number | symbol; - -/** - * Global store of unique values. - * This means that faker should *never* return duplicate values across all API methods when using `Faker.unique` without passing `options.store`. - */ -const GLOBAL_UNIQUE_STORE: Record = {}; - -/** - * Global exclude list of results. - * Defaults to nothing excluded. - */ -const GLOBAL_UNIQUE_EXCLUDE: RecordKey[] = []; - -/** - * Uniqueness compare function. - * Default behavior is to check value as key against object hash. - * - * @param obj The object to check. - * @param key The key to check. - */ -function defaultCompare( - obj: Record, - key: RecordKey -): 0 | -1 { - if (obj[key] === undefined) { - return -1; - } - return 0; -} - -/** - * Logs the given code as an error and throws it. - * Also logs a message for helping the user. - * - * @param startTime The time the execution started. - * @param now The current time. - * @param code The error code. - * @param store The store of unique entries. - * @param currentIterations Current iteration or retries of `unique.exec` (current loop depth). - * - * @throws The given error code with additional text. - */ -function errorMessage( - startTime: number, - now: number, - code: string, - store: Record, - currentIterations: number -): never { - console.error('Error', code); - console.log( - `Found ${Object.keys(store).length} unique entries before throwing error. -retried: ${currentIterations} -total time: ${now - startTime}ms` - ); - throw new FakerError( - `${code} for uniqueness check. - -May not be able to generate any more unique values with current settings. -Try adjusting maxTime or maxRetries parameters for faker.unique().` - ); -} - -/** - * Generates a unique result using the results of the given method. - * Used unique entries will be stored internally and filtered from subsequent calls. - * - * @template Method The type of the method to execute. - * @param method The method used to generate the values. - * @param args The arguments used to call the method. - * @param options The optional options used to configure this method. - * @param options.startTime The time this execution stared. Defaults to `new Date().getTime()`. - * @param options.maxTime The time in milliseconds this method may take before throwing an error. Defaults to `50`. - * @param options.maxRetries The total number of attempts to try before throwing an error. Defaults to `50`. - * @param options.currentIterations The current attempt. Defaults to `0`. - * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. - * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. - * @param options.store The store of unique entries. Defaults to `GLOBAL_UNIQUE_STORE`. - */ -export function exec RecordKey>( - method: Method, - args: Parameters, - options: { - startTime?: number; - maxTime?: number; - maxRetries?: number; - currentIterations?: number; - exclude?: RecordKey | RecordKey[]; - compare?: (obj: Record, key: RecordKey) => 0 | -1; - store?: Record; - } = {} -): ReturnType { - const now = new Date().getTime(); - - const { - startTime = new Date().getTime(), - maxTime = 50, - maxRetries = 50, - compare = defaultCompare, - store = GLOBAL_UNIQUE_STORE, - } = options; - let { exclude = GLOBAL_UNIQUE_EXCLUDE } = options; - options.currentIterations = options.currentIterations ?? 0; - - // Support single exclude argument as string - if (!Array.isArray(exclude)) { - exclude = [exclude]; - } - - // if (options.currentIterations > 0) { - // console.log('iterating', options.currentIterations) - // } - - // console.log(now - startTime) - if (now - startTime >= maxTime) { - return errorMessage( - startTime, - now, - `Exceeded maxTime: ${maxTime}`, - store, - options.currentIterations - ); - } - - if (options.currentIterations >= maxRetries) { - return errorMessage( - startTime, - now, - `Exceeded maxRetries: ${maxRetries}`, - store, - options.currentIterations - ); - } - - // Execute the provided method to find a potential satisfied value. - const result: ReturnType = method.apply(this, args); - - // If the result has not been previously found, add it to the found array and return the value as it's unique. - if (compare(store, result) === -1 && exclude.indexOf(result) === -1) { - store[result] = result; - options.currentIterations = 0; - return result; - } else { - // console.log('conflict', result); - options.currentIterations++; - return exec(method, args, { - ...options, - startTime, - maxTime, - maxRetries, - compare, - exclude, - }); - } -} -- cgit v1.2.3