Skip to content

Commit

Permalink
feat(modifications): Refactor hooks to accept an array of parts
Browse files Browse the repository at this point in the history
* Hook can be single object part or array of parts
* All arrays are processed

#185
  • Loading branch information
FoxxMD committed Sep 10, 2024
1 parent 8940cc9 commit 9b808c8
Show file tree
Hide file tree
Showing 11 changed files with 700 additions and 285 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"dotenv": "^10.0.0",
"express": "^4.17.1",
"express-session": "^1.17.2",
"fast-deep-equal": "^3.1.3",
"fixed-size-list": "^0.3.0",
"formidable": "^3.5",
"gotify": "^1.1.0",
Expand Down
47 changes: 32 additions & 15 deletions src/backend/common/AbstractComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { childLogger, Logger } from "@foxxmd/logging";
import {
cacheFunctions,
} from "@foxxmd/regex-buddy-core";
import deepEqual from 'fast-deep-equal';
import { Simulate } from "react-dom/test-utils";
import { PlayObject } from "../../core/Atomic.js";
import { buildTrackString } from "../../core/StringUtils.js";

import {
configPartsToStrongParts, countRegexes,
Expand All @@ -12,13 +15,14 @@ import { hasNodeNetworkException } from "./errors/NodeErrors.js";
import { hasUpstreamError } from "./errors/UpstreamError.js";
import {
ConditionalSearchAndReplaceRegExp,
PlayTransformParts,
PlayTransformParts, PlayTransformPartsArray,
PlayTransformRules,
TRANSFORM_HOOK,
TransformHook
} from "./infrastructure/Atomic.js";
import { CommonClientConfig } from "./infrastructure/config/client/index.js";
import { CommonSourceConfig } from "./infrastructure/config/source/index.js";
import play = Simulate.play;

export default abstract class AbstractComponent {
requiresAuth: boolean = false;
Expand All @@ -34,8 +38,6 @@ export default abstract class AbstractComponent {
config: CommonClientConfig | CommonSourceConfig;

transformRules!: PlayTransformRules;
// TODO set this based on number of rules?
// we will know how many rules there are at component build time...
regexCache!: ReturnType<typeof cacheFunctions>;

logger: Logger;
Expand Down Expand Up @@ -261,7 +263,7 @@ export default abstract class AbstractComponent {
const getLogger = () => logger !== undefined ? logger : childLogger(this.logger, labels);

try {
let hook: PlayTransformParts<ConditionalSearchAndReplaceRegExp> | undefined;
let hook: PlayTransformPartsArray<ConditionalSearchAndReplaceRegExp> | undefined;

switch (hookType) {
case TRANSFORM_HOOK.preCompare:
Expand All @@ -282,20 +284,35 @@ export default abstract class AbstractComponent {
return play;
}

const [transformedPlay, transformDetails] = transformPlayUsingParts(play, hook, {
logger: getLogger,
regex: {
searchAndReplace: this.regexCache.searchAndReplace,
testMaybeRegex: this.regexCache.testMaybeRegex,
let transformedPlay: PlayObject = play;
const transformDetails: string[] = [];
for(const hookItem of hook) {
const newTransformedPlay = transformPlayUsingParts(transformedPlay, hookItem, {
logger: getLogger,
regex: {
searchAndReplace: this.regexCache.searchAndReplace,
testMaybeRegex: this.regexCache.testMaybeRegex,
}
});
if(!deepEqual(newTransformedPlay, transformedPlay)) {
transformDetails.push(buildTrackString(transformedPlay, {include: ['artist', 'track', 'album']}));
}
});
transformedPlay = newTransformedPlay;
}

const shouldLog = log ?? this.config.options?.playTransform?.log ?? true;
if(shouldLog) {
this.logger.debug({labels: [...labels, hookType]}, transformDetails);
if(transformDetails.length > 0) {
let transformStatements = [`Original: ${buildTrackString(play, {include: ['artist', 'track', 'album']})}`];
const shouldLog = log ?? this.config.options?.playTransform?.log ?? false;
if (shouldLog === true || shouldLog === 'all') {
if(shouldLog === 'all') {
transformStatements = transformStatements.concat(transformDetails.map(x => `=> ${x}`));
} else {
transformStatements.push(`=> ${transformDetails[transformDetails.length - 1]}`);
}
this.logger.debug({labels: [...labels, hookType]}, `Transform Pipeline:\n${transformStatements.join('\n')}`);
}

return transformedPlay;
}
return transformedPlay;
} catch (e) {
getLogger().warn(new Error(`Unexpected error occurred, returning original play.`, {cause: e}));
return play;
Expand Down
32 changes: 23 additions & 9 deletions src/backend/common/infrastructure/Atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,28 +245,42 @@ export type AbstractApiOptions = Record<any, any> & { logger: Logger }

export type keyOmit<T, U extends keyof any> = T & { [P in U]?: never }

export interface ConditionalSearchAndReplaceRegExp extends SearchAndReplaceRegExp {
export interface ConditionalSearchAndReplaceRegExp extends SearchAndReplaceRegExp{
when?: WhenConditionsConfig
}

export type SearchAndReplaceTerm = string | ConditionalSearchAndReplaceRegExp;
export type ConditionalSearchAndReplaceTerm = Omit<ConditionalSearchAndReplaceRegExp, 'test'>

export type SearchAndReplaceTerm = string | ConditionalSearchAndReplaceTerm;

export type PlayTransformParts<T> = PlayTransformPartsAtomic<T[]> & { when?: WhenConditionsConfig };

export type PlayTransformPartsArray<T> = PlayTransformParts<T>[];

export type PlayTransformPartsConfig<T> = PlayTransformPartsArray<T> | PlayTransformParts<T>;

export interface PlayTransformPartsAtomic<T> {
title?: T
artists?: T
album?: T
}

export interface PlayTransformHooks<T> {
preCompare?: PlayTransformParts<T>
export interface PlayTransformHooksConfig<T> {
preCompare?: PlayTransformPartsConfig<T>
compare?: {
candidate?: PlayTransformPartsConfig<T>
existing?: PlayTransformPartsConfig<T>
}
postCompare?: PlayTransformPartsConfig<T>
}

export interface PlayTransformHooks<T> extends PlayTransformHooksConfig<T> {
preCompare?: PlayTransformPartsArray<T>
compare?: {
candidate?: PlayTransformParts<T>
existing?: PlayTransformParts<T>
candidate?: PlayTransformPartsArray<T>
existing?: PlayTransformPartsArray<T>
}
postCompare?: PlayTransformParts<T>
postCompare?: PlayTransformPartsArray<T>
}

export type PlayTransformRules = PlayTransformHooks<ConditionalSearchAndReplaceRegExp>
Expand All @@ -278,8 +292,8 @@ export const TRANSFORM_HOOK = {
existing: 'existing' as TransformHook,
postCompare: 'postCompare' as TransformHook,
}
export type PlayTransformConfig = PlayTransformHooks<SearchAndReplaceTerm>;
export type PlayTransformOptions = PlayTransformConfig & { log?: boolean }
export type PlayTransformConfig = PlayTransformHooksConfig<SearchAndReplaceTerm>;
export type PlayTransformOptions = PlayTransformConfig & { log?: boolean | 'all' }

export type WhenParts<T> = PlayTransformPartsAtomic<T>;

Expand Down
137 changes: 98 additions & 39 deletions src/backend/common/schema/aio-client.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,40 @@
"title": "CommonClientOptions",
"type": "object"
},
"ConditionalSearchAndReplaceTerm": {
"properties": {
"replace": {
"description": "The replacement string/value to use when search is found\n\nThis can be a literal string like `'replace with this`, an empty string to remove the search value (`''`), or a special regex value\n\nSee replacement here for more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",
"title": "replace",
"type": "string"
},
"search": {
"anyOf": [
{
"$ref": "#/definitions/RegExp"
},
{
"type": "string"
}
],
"description": "The search value to test for\n\nCan be a normal string (converted to a case-sensitive literal) or a valid regular expression as a string, or an actual RegExp object",
"title": "search"
},
"when": {
"items": {
"$ref": "#/definitions/WhenParts<string>"
},
"title": "when",
"type": "array"
}
},
"required": [
"replace",
"search"
],
"title": "ConditionalSearchAndReplaceTerm",
"type": "object"
},
"LastfmClientAIOConfig": {
"properties": {
"configureAs": {
Expand Down Expand Up @@ -454,23 +488,23 @@
"compare": {
"properties": {
"candidate": {
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>",
"$ref": "#/definitions/PlayTransformPartsConfig<SearchAndReplaceTerm>",
"title": "candidate"
},
"existing": {
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>",
"$ref": "#/definitions/PlayTransformPartsConfig<SearchAndReplaceTerm>",
"title": "existing"
}
},
"title": "compare",
"type": "object"
},
"postCompare": {
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>",
"$ref": "#/definitions/PlayTransformPartsConfig<SearchAndReplaceTerm>",
"title": "postCompare"
},
"preCompare": {
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>",
"$ref": "#/definitions/PlayTransformPartsConfig<SearchAndReplaceTerm>",
"title": "preCompare"
}
},
Expand All @@ -485,8 +519,12 @@
{
"properties": {
"log": {
"title": "log",
"type": "boolean"
"enum": [
"all",
false,
true
],
"title": "log"
}
},
"type": "object"
Expand All @@ -495,6 +533,26 @@
"title": "PlayTransformOptions"
},
"PlayTransformParts<SearchAndReplaceTerm>": {
"allOf": [
{
"$ref": "#/definitions/PlayTransformPartsAtomic<SearchAndReplaceTerm[]>"
},
{
"properties": {
"when": {
"items": {
"$ref": "#/definitions/WhenParts<string>"
},
"title": "when",
"type": "array"
}
},
"type": "object"
}
],
"title": "PlayTransformParts<SearchAndReplaceTerm>"
},
"PlayTransformPartsAtomic<SearchAndReplaceTerm[]>": {
"properties": {
"album": {
"items": {
Expand All @@ -518,9 +576,23 @@
"type": "array"
}
},
"title": "PlayTransformParts<SearchAndReplaceTerm>",
"title": "PlayTransformPartsAtomic<SearchAndReplaceTerm[]>",
"type": "object"
},
"PlayTransformPartsConfig<SearchAndReplaceTerm>": {
"anyOf": [
{
"items": {
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>"
},
"type": "array"
},
{
"$ref": "#/definitions/PlayTransformParts<SearchAndReplaceTerm>"
}
],
"title": "PlayTransformPartsConfig<SearchAndReplaceTerm>"
},
"RegExp": {
"properties": {
"dotAll": {
Expand Down Expand Up @@ -603,47 +675,34 @@
"title": "RequestRetryOptions",
"type": "object"
},
"SearchAndReplaceRegExp": {
"properties": {
"replace": {
"description": "The replacement string/value to use when search is found\n\nThis can be a literal string like `'replace with this`, an empty string to remove the search value (`''`), or a special regex value\n\nSee replacement here for more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",
"title": "replace",
"type": "string"
},
"search": {
"anyOf": [
{
"$ref": "#/definitions/RegExp"
},
{
"type": "string"
}
],
"description": "The search value to test for\n\nCan be a normal string (converted to a case-sensitive literal) or a valid regular expression as a string, or an actual RegExp object\n\nEX `[\"find this string\", \"/some string*\\/ig\"]`",
"examples": [
"find this string",
"/some string*/ig"
],
"title": "search"
}
},
"required": [
"replace",
"search"
],
"title": "SearchAndReplaceRegExp",
"type": "object"
},
"SearchAndReplaceTerm": {
"anyOf": [
{
"$ref": "#/definitions/SearchAndReplaceRegExp"
"$ref": "#/definitions/ConditionalSearchAndReplaceTerm"
},
{
"type": "string"
}
],
"title": "SearchAndReplaceTerm"
},
"WhenParts<string>": {
"properties": {
"album": {
"title": "album",
"type": "string"
},
"artists": {
"title": "artists",
"type": "string"
},
"title": {
"title": "title",
"type": "string"
}
},
"title": "WhenParts<string>",
"type": "object"
}
},
"properties": {
Expand Down
Loading

0 comments on commit 9b808c8

Please sign in to comment.