From 34eeb119307704839c1b116df64547adfe21f83b Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Mon, 27 Jun 2022 17:16:42 +0200 Subject: [PATCH] cleanup preference repo --- .../preference-common.service.ts | 23 +++++++++++-------- .../preference-defaults.service.ts | 3 ++- .../sys-preference-db.service.ts | 13 ++++++----- .../usr-preference-db.service.ts | 10 ++++---- backend/src/models/util/mutex-fallback.ts | 22 ++++++++++-------- 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/backend/src/collections/preference-db/preference-common.service.ts b/backend/src/collections/preference-db/preference-common.service.ts index 8fa5fd9..6e4acec 100644 --- a/backend/src/collections/preference-db/preference-common.service.ts +++ b/backend/src/collections/preference-db/preference-common.service.ts @@ -2,13 +2,13 @@ import { Injectable, Logger } from '@nestjs/common'; import { DecodedPref, PrefValueType, - PrefValueTypeStrings, + PrefValueTypeStrings } from 'picsur-shared/dist/dto/preferences.dto'; import { AsyncFailable, Fail, Failable, - HasFailed, + HasFailed } from 'picsur-shared/dist/types'; type Enum = Record; @@ -16,7 +16,7 @@ type EnumValue = E[keyof E]; type PrefValueTypeType = { [key in EnumValue]: PrefValueTypeStrings; }; -type KeyValuePref = { +type EncodedPref = { key: string; value: string; }; @@ -25,8 +25,13 @@ type KeyValuePref = { export class PreferenceCommonService { private readonly logger = new Logger('PreferenceCommonService'); - public validateAndUnpackPref( - preference: KeyValuePref, + // Preferences values are only validated upon encoding, not decoding + // The preference keys are always validated + + // E is either the SysPreference or the UsrPreference enum + // the pref value types is the object containing the type of each key in E + public DecodePref( + preference: EncodedPref, prefType: E, prefValueTypes: PrefValueTypeType, ): Failable { @@ -58,17 +63,17 @@ export class PreferenceCommonService { return Fail('Invalid preference value'); } - public async validatePref( + public async EncodePref( key: string, value: PrefValueType, prefType: E, prefValueTypes: PrefValueTypeType, - ): AsyncFailable { + ): AsyncFailable { const validatedKey = this.validatePrefKey(key, prefType); if (HasFailed(validatedKey)) return validatedKey; const valueType = prefValueTypes[validatedKey]; - const validatedValue = this.validateAndPackPrefValue(value, valueType); + const validatedValue = this.encodePrefValue(value, valueType); if (HasFailed(validatedValue)) return validatedValue; return { @@ -89,7 +94,7 @@ export class PreferenceCommonService { return key as V; } - public validateAndPackPrefValue( + public encodePrefValue( value: PrefValueType, expectedType: PrefValueTypeStrings, ): Failable { diff --git a/backend/src/collections/preference-db/preference-defaults.service.ts b/backend/src/collections/preference-db/preference-defaults.service.ts index 670ac11..8976055 100644 --- a/backend/src/collections/preference-db/preference-defaults.service.ts +++ b/backend/src/collections/preference-db/preference-defaults.service.ts @@ -5,8 +5,9 @@ import { UsrPreference } from 'picsur-shared/dist/dto/usr-preferences.enum'; import { generateRandomString } from 'picsur-shared/dist/util/random'; import { EarlyJwtConfigService } from '../../config/early/early-jwt.config.service'; -// This specific service is used to store default values for system preferences +// This specific service holds the default values for system and user preferences // It needs to be in a service because the values depend on the environment +// This environment is not loaded outside of services @Injectable() export class PreferenceDefaultsService { diff --git a/backend/src/collections/preference-db/sys-preference-db.service.ts b/backend/src/collections/preference-db/sys-preference-db.service.ts index d459c3c..8a1574d 100644 --- a/backend/src/collections/preference-db/sys-preference-db.service.ts +++ b/backend/src/collections/preference-db/sys-preference-db.service.ts @@ -36,7 +36,7 @@ export class SysPreferenceService { value: PrefValueType, ): AsyncFailable { // Validate - let sysPreference = await this.validateSysPref(key, value); + let sysPreference = await this.encodeSysPref(key, value); if (HasFailed(sysPreference)) return sysPreference; // Set @@ -49,7 +49,6 @@ export class SysPreferenceService { return Fail(e); } - // Return return { key: sysPreference.key, value, @@ -63,8 +62,9 @@ export class SysPreferenceService { let validatedKey = this.prefCommon.validatePrefKey(key, SysPreference); if (HasFailed(validatedKey)) return validatedKey; + // See the comment in 'mutex-fallback.ts' for why we are using a mutex here return MutexFallBack( - 'fetchSysPrefrence', + `fetchSysPrefrence-${key}`, async () => { let existing: ESysPreferenceBackend | null; try { @@ -84,7 +84,7 @@ export class SysPreferenceService { } // Return - return this.prefCommon.validateAndUnpackPref( + return this.prefCommon.DecodePref( result.data, SysPreference, SysPreferenceValueTypes, @@ -106,6 +106,7 @@ export class SysPreferenceService { return this.getPreferencePinned(key, 'boolean') as AsyncFailable; } + // Get a preference that will be pinned to a specified type private async getPreferencePinned( key: string, type: PrefValueTypeStrings, @@ -137,11 +138,11 @@ export class SysPreferenceService { return this.setPreference(key, this.defaultsService.sysDefaults[key]()); } - private async validateSysPref( + private async encodeSysPref( key: string, value: PrefValueType, ): AsyncFailable { - const validated = await this.prefCommon.validatePref( + const validated = await this.prefCommon.EncodePref( key, value, SysPreference, diff --git a/backend/src/collections/preference-db/usr-preference-db.service.ts b/backend/src/collections/preference-db/usr-preference-db.service.ts index 4a56208..0b9b117 100644 --- a/backend/src/collections/preference-db/usr-preference-db.service.ts +++ b/backend/src/collections/preference-db/usr-preference-db.service.ts @@ -37,7 +37,7 @@ export class UsrPreferenceService { value: PrefValueType, ): AsyncFailable { // Validate - let usrPreference = await this.validatePref(userid, key, value); + let usrPreference = await this.encodeUsrPref(userid, key, value); if (HasFailed(usrPreference)) return usrPreference; // Set @@ -68,6 +68,7 @@ export class UsrPreferenceService { let validatedKey = this.prefCommon.validatePrefKey(key, UsrPreference); if (HasFailed(validatedKey)) return validatedKey; + // See the comment in 'mutex-fallback.ts' for why we are using a mutex here return MutexFallBack( 'fetchUsrPrefrence', async () => { @@ -89,7 +90,7 @@ export class UsrPreferenceService { } // Return - const unpacked = this.prefCommon.validateAndUnpackPref( + const unpacked = this.prefCommon.DecodePref( result.data, UsrPreference, UsrPreferenceValueTypes, @@ -137,6 +138,7 @@ export class UsrPreferenceService { ) as AsyncFailable; } + // Get a preference that will be pinned to a specified type private async getPreferencePinned( userid: string, key: string, @@ -176,12 +178,12 @@ export class UsrPreferenceService { ); } - private async validatePref( + private async encodeUsrPref( userid: string, key: string, value: PrefValueType, ): AsyncFailable { - const validated = await this.prefCommon.validatePref( + const validated = await this.prefCommon.EncodePref( key, value, UsrPreference, diff --git a/backend/src/models/util/mutex-fallback.ts b/backend/src/models/util/mutex-fallback.ts index ada0c61..9b5b648 100644 --- a/backend/src/models/util/mutex-fallback.ts +++ b/backend/src/models/util/mutex-fallback.ts @@ -1,11 +1,12 @@ /* -This function is necessary to make sure that a default isnt generated multiple times at the same time. +This function is necessary to make sure that a default isn't generated multiple times at the same time. Doing that will cause errors. -An example is the jwt secret value, its value is requested aroun 3 times at the same time while starting. +An example is the jwt secret value, it's value is requested around 3 times at the same time while starting. So when the program was started for the first time, each request returned a different secret. -This function will first try and see if its first function returns a value, if it does, it will return that value. +This function will try and see if its first function returns a value, if it does, it will return that value. +(A Failure object will be seen as a value, only null and undefined are counted as non-values) If not it will execute a fallback function, which usually is a function that populates a default value. After that is done it will retry the first function again. */ @@ -20,21 +21,22 @@ export async function MutexFallBack< const try_it = await mainFunc(); if (try_it !== undefined && try_it !== null) return try_it; + // Check if a fallback is already running, if so wait on that if (fallBackMap[key] !== undefined) { await fallBackMap[key]; + + // When it is done, try again return MutexFallBack(key, mainFunc, fallBackFunc); } + // No fallback is running, start one const fallBackPromise = fallBackFunc(); + // Save the running fallback, and make sure it is cleared when it is done fallBackMap[key] = fallBackPromise; - fallBackMap[key] - .then(() => { - delete fallBackMap[key]; - }) - .catch(() => { - delete fallBackMap[key]; - }); + fallBackMap[key].finally(() => { + delete fallBackMap[key]; + }); return fallBackPromise; }