diff --git a/src/api.ts b/src/api.ts deleted file mode 100644 index 0ed8dc7..0000000 --- a/src/api.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { KyHttpClient } from "./__generated__/ky-client"; -import { Api as RadarrApi } from "./__generated__/radarr/Api"; -import { Api as ReadarrApi } from "./__generated__/readarr/Api"; -import { Api as SonarrApi } from "./__generated__/sonarr/Api"; -import { Api as WhisparrApi } from "./__generated__/whisparr/Api"; -import { logger } from "./logger"; -import { ArrType } from "./types/common.types"; - -let sonarrClient: SonarrApi | undefined; -let radarrClient: RadarrApi | undefined; -let whisparrClient: WhisparrApi | undefined; -let readarrClient: ReadarrApi | undefined; - -export const unsetApi = () => { - sonarrClient = undefined; - radarrClient = undefined; - whisparrClient = undefined; - readarrClient = undefined; -}; - -export const getArrApi = (): SonarrApi | RadarrApi | WhisparrApi | ReadarrApi => { - const client = sonarrClient || radarrClient || whisparrClient || readarrClient; - - if (client) { - return client; - } - - throw new Error("Please configure API first."); -}; - -const validateParams = (url: string, apiKey: string, arrType: ArrType) => { - const arrLabel = arrType.toLowerCase(); - - if (!url) { - const message = `URL not correctly configured for ${arrLabel} API!`; - logger.error(message); - throw new Error(message); - } - if (!apiKey) { - const message = `API Key not correctly configured for ${arrLabel} API!`; - logger.error(message); - throw new Error(message); - } -}; - -const handleErrorApi = (error: any, arrType: ArrType) => { - let message; - const arrLabel = arrType.toLowerCase(); - const causeError = error?.cause?.message || error?.cause?.errors?.map((e: any) => e.message).join(";") || undefined; - - const errorMessage = (error.message && `Message: ${error.message}`) || ""; - const causeMessage = (causeError && `- Cause: ${causeError}`) || ""; - - logger.error(`Error configuring ${arrLabel} API. ${errorMessage} ${causeMessage}`); - - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - message = `Unable to retrieve data from ${arrLabel} API. Server responded with status code ${error.response.status}: ${error.response.statusText}. Please check the API server status or your request parameters.`; - } else { - // Something happened in setting up the request that triggered an Error - message = `An unexpected error occurred while setting up the ${arrLabel} request: ${errorMessage} ${causeMessage}. Please try again.`; - } - - throw new Error(message); -}; - -export const configureApi = async (type: ArrType, baseUrl: string, apiKey: string) => { - unsetApi(); - validateParams(baseUrl, apiKey, type); - - const httpClient = new KyHttpClient({ - headers: { - "X-Api-Key": apiKey, - }, - prefixUrl: baseUrl, - }); - - let api; - - switch (type) { - case "SONARR": - api = new SonarrApi(httpClient); - sonarrClient = api; - break; - case "RADARR": - api = new RadarrApi(httpClient); - radarrClient = api; - break; - case "WHISPARR": - api = new WhisparrApi(httpClient); - whisparrClient = api; - break; - case "READARR": - api = new ReadarrApi(httpClient); - readarrClient = api; - break; - default: - throw new Error(`Invalid API type: ${type}`); - } - - try { - await api.v3MetadataList(); - } catch (error: any) { - handleErrorApi(error, type); - } - - return api; -}; diff --git a/src/clients/radarr-client.ts b/src/clients/radarr-client.ts new file mode 100644 index 0000000..e0a3492 --- /dev/null +++ b/src/clients/radarr-client.ts @@ -0,0 +1,94 @@ +import { KyHttpClient } from "../__generated__/ky-client"; +import { MergedCustomFormatResource } from "../__generated__/mergedTypes"; +import { Api } from "../__generated__/radarr/Api"; +import { logger } from "../logger"; +import { IArrClient, validateClientParams } from "./unified-client"; + +export class RadarrClient implements IArrClient { + private api!: Api; + + constructor(baseUrl: string, apiKey: string) { + this.initialize(baseUrl, apiKey); + } + + private initialize(baseUrl: string, apiKey: string) { + validateClientParams(baseUrl, apiKey, "RADARR"); + + const httpClient = new KyHttpClient({ + headers: { + "X-Api-Key": apiKey, + }, + prefixUrl: baseUrl, + }); + + this.api = new Api(httpClient); + } + + // Quality Management + getQualityDefinitions() { + return this.api.v3QualitydefinitionList(); + } + + updateQualityDefinitions(definitions: any) { + return this.api.v3QualitydefinitionUpdateUpdate(definitions); + } + + // Quality Profiles + getQualityProfiles() { + return this.api.v3QualityprofileList(); + } + + createQualityProfile(profile: any) { + return this.api.v3QualityprofileCreate(profile); + } + + updateQualityProfile(id: string, profile: any) { + return this.api.v3QualityprofileUpdate(id, profile); + } + + // Custom Formats + getCustomFormats() { + return this.api.v3CustomformatList(); + } + + createCustomFormat(format: MergedCustomFormatResource) { + return this.api.v3CustomformatCreate(format); + } + + updateCustomFormat(id: string, format: MergedCustomFormatResource) { + return this.api.v3CustomformatUpdate(id, format); + } + + deleteCustomFormat(id: string) { + return this.api.v3CustomformatDelete(+id); + } + + // Metadata Profiles + async getMetadataProfiles() { + throw new Error("Metadata profiles are not supported in Radarr"); + } + + async createMetadataProfile(profile: any) { + throw new Error("Metadata profiles are not supported in Radarr"); + } + + async updateMetadataProfile(id: number, profile: any) { + throw new Error("Metadata profiles are not supported in Radarr"); + } + + // System/Health Check + getSystemStatus() { + return this.api.v3SystemStatusList(); + } + + async testConnection() { + try { + await this.api.v3HealthList(); + } catch (error) { + logger.error(error); + return false; + } + + return true; + } +} diff --git a/src/clients/readarr-client.ts b/src/clients/readarr-client.ts new file mode 100644 index 0000000..e88aa3f --- /dev/null +++ b/src/clients/readarr-client.ts @@ -0,0 +1,94 @@ +import { KyHttpClient } from "../__generated__/ky-client"; +import { MergedCustomFormatResource } from "../__generated__/mergedTypes"; +import { Api } from "../__generated__/readarr/Api"; +import { logger } from "../logger"; +import { IArrClient, validateClientParams } from "./unified-client"; + +export class ReadarrClient implements IArrClient { + private api!: Api; + + constructor(baseUrl: string, apiKey: string) { + this.initialize(baseUrl, apiKey); + } + + private initialize(baseUrl: string, apiKey: string) { + validateClientParams(baseUrl, apiKey, "READARR"); + + const httpClient = new KyHttpClient({ + headers: { + "X-Api-Key": apiKey, + }, + prefixUrl: baseUrl, + }); + + this.api = new Api(httpClient); + } + + // Quality Management + getQualityDefinitions() { + return this.api.v1QualitydefinitionList(); + } + + updateQualityDefinitions(definitions: any) { + return this.api.v1QualitydefinitionUpdateUpdate(definitions); + } + + // Quality Profiles + getQualityProfiles() { + return this.api.v1QualityprofileList(); + } + + createQualityProfile(profile: any) { + return this.api.v1QualityprofileCreate(profile); + } + + updateQualityProfile(id: string, profile: any) { + return this.api.v1QualityprofileUpdate(id, profile); + } + + // Custom Formats + getCustomFormats() { + return this.api.v1CustomformatList(); + } + + createCustomFormat(format: MergedCustomFormatResource) { + return this.api.v1CustomformatCreate(format); + } + + updateCustomFormat(id: string, format: MergedCustomFormatResource) { + return this.api.v1CustomformatUpdate(id, format); + } + + deleteCustomFormat(id: string) { + return this.api.v1CustomformatDelete(+id); + } + + // Metadata Profiles + async getMetadataProfiles() { + return this.api.v1MetadataprofileList(); + } + + async createMetadataProfile(profile: any) { + return this.api.v1MetadataprofileCreate(profile); + } + + async updateMetadataProfile(id: number, profile: any) { + return this.api.v1MetadataprofileUpdate(id.toString(), profile); + } + + // System/Health Check + getSystemStatus() { + return this.api.v1SystemStatusList(); + } + + async testConnection() { + try { + await this.api.v1HealthList(); + } catch (error) { + logger.error(error); + return false; + } + + return true; + } +} diff --git a/src/clients/sonarr-client.ts b/src/clients/sonarr-client.ts new file mode 100644 index 0000000..b901708 --- /dev/null +++ b/src/clients/sonarr-client.ts @@ -0,0 +1,94 @@ +import { KyHttpClient } from "../__generated__/ky-client"; +import { MergedCustomFormatResource } from "../__generated__/mergedTypes"; +import { Api } from "../__generated__/sonarr/Api"; +import { logger } from "../logger"; +import { IArrClient, validateClientParams } from "./unified-client"; + +export class SonarrClient implements IArrClient { + private api!: Api; + + constructor(baseUrl: string, apiKey: string) { + this.initialize(baseUrl, apiKey); + } + + private initialize(baseUrl: string, apiKey: string) { + validateClientParams(baseUrl, apiKey, "SONARR"); + + const httpClient = new KyHttpClient({ + headers: { + "X-Api-Key": apiKey, + }, + prefixUrl: baseUrl, + }); + + this.api = new Api(httpClient); + } + + // Quality Management + getQualityDefinitions() { + return this.api.v3QualitydefinitionList(); + } + + updateQualityDefinitions(definitions: any) { + return this.api.v3QualitydefinitionUpdateUpdate(definitions); + } + + // Quality Profiles + getQualityProfiles() { + return this.api.v3QualityprofileList(); + } + + createQualityProfile(profile: any) { + return this.api.v3QualityprofileCreate(profile); + } + + updateQualityProfile(id: string, profile: any) { + return this.api.v3QualityprofileUpdate(id, profile); + } + + // Custom Formats + getCustomFormats() { + return this.api.v3CustomformatList(); + } + + createCustomFormat(format: MergedCustomFormatResource) { + return this.api.v3CustomformatCreate(format); + } + + updateCustomFormat(id: string, format: MergedCustomFormatResource) { + return this.api.v3CustomformatUpdate(id, format); + } + + deleteCustomFormat(id: string) { + return this.api.v3CustomformatDelete(+id); + } + + // Metadata Profiles + async getMetadataProfiles() { + throw new Error("Metadata profiles are not supported in Sonarr"); + } + + async createMetadataProfile(profile: any) { + throw new Error("Metadata profiles are not supported in Sonarr"); + } + + async updateMetadataProfile(id: number, profile: any) { + throw new Error("Metadata profiles are not supported in Sonarr"); + } + + // System/Health Check + getSystemStatus() { + return this.api.v3SystemStatusList(); + } + + async testConnection() { + try { + await this.api.v3HealthList(); + } catch (error) { + logger.error(error); + return false; + } + + return true; + } +} diff --git a/src/clients/unified-client.ts b/src/clients/unified-client.ts new file mode 100644 index 0000000..263e9ce --- /dev/null +++ b/src/clients/unified-client.ts @@ -0,0 +1,193 @@ +import { MergedCustomFormatResource } from "../__generated__/mergedTypes"; +import { logger } from "../logger"; +import { ArrType } from "../types/common.types"; +import { RadarrClient } from "./radarr-client"; +import { ReadarrClient } from "./readarr-client"; +import { SonarrClient } from "./sonarr-client"; +import { WhisparrClient } from "./whisparr-client"; + +let unifiedClient: UnifiedClient | undefined; + +export const unsetApi = () => { + unifiedClient = undefined; +}; + +export const getUnifiedClient = (): UnifiedClient => { + if (!unifiedClient) { + throw new Error("Please configure API first."); + } + return unifiedClient; +}; + +export const validateClientParams = (url: string, apiKey: string, arrType: ArrType) => { + const arrLabel = arrType.toLowerCase(); + + if (!url) { + const message = `URL not correctly configured for ${arrLabel} API!`; + logger.error(message); + throw new Error(message); + } + if (!apiKey) { + const message = `API Key not correctly configured for ${arrLabel} API!`; + logger.error(message); + throw new Error(message); + } +}; + +export const handleErrorApi = (error: any, arrType: ArrType) => { + let message; + const arrLabel = arrType.toLowerCase(); + const causeError = error?.cause?.message || error?.cause?.errors?.map((e: any) => e.message).join(";") || undefined; + + const errorMessage = (error.message && `Message: ${error.message}`) || ""; + const causeMessage = (causeError && `- Cause: ${causeError}`) || ""; + + logger.error(`Error configuring ${arrLabel} API. ${errorMessage} ${causeMessage}`); + + if (error.response) { + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + message = `Unable to retrieve data from ${arrLabel} API. Server responded with status code ${error.response.status}: ${error.response.statusText}. Please check the API server status or your request parameters.`; + } else { + // Something happened in setting up the request that triggered an Error + message = `An unexpected error occurred while setting up the ${arrLabel} request: ${errorMessage} ${causeMessage}. Please try again.`; + } + + throw new Error(message); +}; + +export const configureApi = async (type: ArrType, baseUrl: string, apiKey: string) => { + unsetApi(); + + unifiedClient = new UnifiedClient(type, baseUrl, apiKey); + + try { + await unifiedClient.testConnection(); + } catch (error: any) { + handleErrorApi(error, type); + } + + return unifiedClient; +}; + +export interface IArrClient { + // Quality Management + getQualityDefinitions(): Promise; + updateQualityDefinitions(definitions: any): Promise; + + // Quality Profiles + getQualityProfiles(): Promise; + createQualityProfile(profile: any): Promise; + updateQualityProfile(id: string, profile: any): Promise; + + // Custom Formats + getCustomFormats(): Promise; + createCustomFormat(format: MergedCustomFormatResource): Promise; + updateCustomFormat(id: string, format: MergedCustomFormatResource): Promise; + deleteCustomFormat(id: string): Promise; + + // Metadata Profiles (Readarr-specific) + getMetadataProfiles(): Promise; + createMetadataProfile(profile: any): Promise; + updateMetadataProfile(id: number, profile: any): Promise; + + // System/Health Check + getSystemStatus(): Promise; + testConnection(): Promise; +} + +export class UnifiedClient implements IArrClient { + private api!: IArrClient; + private type: ArrType; + + constructor(type: ArrType, baseUrl: string, apiKey: string) { + this.type = type; + + switch (type) { + case "SONARR": + this.api = new SonarrClient(baseUrl, apiKey); + break; + case "RADARR": + this.api = new RadarrClient(baseUrl, apiKey); + break; + case "READARR": + this.api = new ReadarrClient(baseUrl, apiKey); + break; + case "WHISPARR": + this.api = new WhisparrClient(baseUrl, apiKey); + break; + default: + throw new Error(`Invalid API type: ${type}`); + } + } + + async getQualityDefinitions() { + return await this.api.getQualityDefinitions(); + } + + async updateQualityDefinitions(definitions: any) { + return await this.api.updateQualityDefinitions(definitions); + } + + async createQualityProfile(profile: any) { + return await this.api.createQualityProfile(profile); + } + + async getQualityProfiles() { + return await this.api.getQualityProfiles(); + } + + async updateQualityProfile(id: string, profile: any) { + return await this.api.updateQualityProfile(id, profile); + } + + // Readarr-specific methods + async createMetadataProfile(profile: any) { + return await this.api.createMetadataProfile(profile); + } + + async getMetadataProfiles() { + return await this.api.getMetadataProfiles(); + } + + async updateMetadataProfile(id: number, profile: any) { + return await this.api.updateMetadataProfile(id, profile); + } + + async getCustomFormats() { + return await this.api.getCustomFormats(); + } + + async createCustomFormat(format: MergedCustomFormatResource) { + return await this.api.createCustomFormat(format); + } + + async updateCustomFormat(id: string, format: MergedCustomFormatResource) { + return await this.api.updateCustomFormat(id, format); + } + + async deleteCustomFormat(id: string) { + return await this.api.deleteCustomFormat(id); + } + + async getSystemStatus() { + return await this.api.getSystemStatus(); + } + + async testConnection() { + return await this.api.testConnection(); + } + + // Helper method to check if a specific feature is supported + supportsFeature(feature: "metadataProfiles" | "qualityProfiles" | "qualityDefinitions"): boolean { + switch (feature) { + case "metadataProfiles": + return this.type === "READARR"; + case "qualityProfiles": + case "qualityDefinitions": + return true; // Supported by all types + default: + return false; + } + } +} diff --git a/src/clients/whisparr-client.ts b/src/clients/whisparr-client.ts new file mode 100644 index 0000000..4e7b90c --- /dev/null +++ b/src/clients/whisparr-client.ts @@ -0,0 +1,94 @@ +import { KyHttpClient } from "../__generated__/ky-client"; +import { MergedCustomFormatResource } from "../__generated__/mergedTypes"; +import { Api } from "../__generated__/whisparr/Api"; +import { logger } from "../logger"; +import { IArrClient, validateClientParams } from "./unified-client"; + +export class WhisparrClient implements IArrClient { + private api!: Api; + + constructor(baseUrl: string, apiKey: string) { + this.initialize(baseUrl, apiKey); + } + + private initialize(baseUrl: string, apiKey: string) { + validateClientParams(baseUrl, apiKey, "WHISPARR"); + + const httpClient = new KyHttpClient({ + headers: { + "X-Api-Key": apiKey, + }, + prefixUrl: baseUrl, + }); + + this.api = new Api(httpClient); + } + + // Quality Management + getQualityDefinitions() { + return this.api.v3QualitydefinitionList(); + } + + updateQualityDefinitions(definitions: any) { + return this.api.v3QualitydefinitionUpdateUpdate(definitions); + } + + // Quality Profiles + getQualityProfiles() { + return this.api.v3QualityprofileList(); + } + + createQualityProfile(profile: any) { + return this.api.v3QualityprofileCreate(profile); + } + + updateQualityProfile(id: string, profile: any) { + return this.api.v3QualityprofileUpdate(id, profile); + } + + // Custom Formats + getCustomFormats() { + return this.api.v3CustomformatList(); + } + + createCustomFormat(format: MergedCustomFormatResource) { + return this.api.v3CustomformatCreate(format); + } + + updateCustomFormat(id: string, format: MergedCustomFormatResource) { + return this.api.v3CustomformatUpdate(id, format); + } + + deleteCustomFormat(id: string) { + return this.api.v3CustomformatDelete(+id); + } + + // Metadata Profiles + async getMetadataProfiles() { + throw new Error("Metadata profiles are not supported in Whisparr"); + } + + async createMetadataProfile(profile: any) { + throw new Error("Metadata profiles are not supported in Whisparr"); + } + + async updateMetadataProfile(id: number, profile: any) { + throw new Error("Metadata profiles are not supported in Whisparr"); + } + + // System/Health Check + getSystemStatus() { + return this.api.v3SystemStatusList(); + } + + async testConnection() { + try { + await this.api.v3HealthList(); + } catch (error) { + logger.error(error); + return false; + } + + return true; + } +} diff --git a/src/custom-formats.ts b/src/custom-formats.ts index cb9ff52..3372c4d 100644 --- a/src/custom-formats.ts +++ b/src/custom-formats.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { MergedCustomFormatResource } from "./__generated__/mergedTypes"; -import { getArrApi } from "./api"; +import { getUnifiedClient } from "./clients/unified-client"; import { getConfig } from "./config"; import { logger } from "./logger"; import { CFProcessing, ConfigarrCF } from "./types/common.types"; @@ -10,11 +10,11 @@ import { TrashCF } from "./types/trashguide.types"; import { IS_DRY_RUN, IS_LOCAL_SAMPLE_MODE, compareCustomFormats, loadJsonFile, mapImportCfToRequestCf, toCarrCF } from "./util"; export const deleteAllCustomFormats = async () => { - const api = getArrApi(); - const cfOnServer = await api.v3CustomformatList(); + const api = getUnifiedClient(); + const cfOnServer = await api.getCustomFormats(); for (const cf of cfOnServer) { - await api.v3CustomformatDelete(cf.id!); + await api.deleteCustomFormat(cf.id + ""); logger.info(`Deleted CF: '${cf.name}'`); } }; @@ -23,8 +23,8 @@ export const loadServerCustomFormats = async (): Promise(path.resolve(__dirname, "../tests/samples/cfs.json")); } - const api = getArrApi(); - const cfOnServer = await api.v3CustomformatList(); + const api = getUnifiedClient(); + const cfOnServer = await api.getCustomFormats(); return cfOnServer; }; @@ -34,7 +34,7 @@ export const manageCf = async ( cfsToManage: Set, ) => { const { carrIdMapping: trashIdToObject } = cfProcessing; - const api = getArrApi(); + const api = getUnifiedClient(); let updatedCFs: MergedCustomFormatResource[] = []; let errorCFs: string[] = []; @@ -45,7 +45,7 @@ export const manageCf = async ( const tr = trashIdToObject.get(carrId); if (!tr) { - logger.info(`TrashID to manage ${carrId} does not exists`); + logger.warn(`TrashID to manage ${carrId} does not exists`); errorCFs.push(carrId); return; } @@ -64,7 +64,7 @@ export const manageCf = async ( logger.info(`DryRun: Would update CF: ${existingCf.id} - ${existingCf.name}`); updatedCFs.push(existingCf); } else { - const updatedCf = await api.v3CustomformatUpdate(existingCf.id + "", { + const updatedCf = await api.updateCustomFormat(existingCf.id + "", { id: existingCf.id, ...tr.requestConfig, }); @@ -85,7 +85,7 @@ export const manageCf = async ( if (IS_DRY_RUN) { logger.info(`Would create CF: ${tr.requestConfig.name}`); } else { - const createResult = await api.v3CustomformatCreate(tr.requestConfig); + const createResult = await api.createCustomFormat(tr.requestConfig); logger.info(`Created CF ${tr.requestConfig.name}`); createCFs.push(createResult); } diff --git a/src/index.ts b/src/index.ts index 96bc1ab..decfc10 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import "dotenv/config"; import fs from "node:fs"; import { MergedCustomFormatResource } from "./__generated__/mergedTypes"; -import { configureApi, getArrApi, unsetApi } from "./api"; +import { configureApi, getUnifiedClient, unsetApi } from "./clients/unified-client"; import { getConfig, validateConfig } from "./config"; import { calculateCFsToManage, loadCFFromConfig, loadLocalCfs, loadServerCustomFormats, manageCf, mergeCfSources } from "./custom-formats"; import { loadLocalRecyclarrTemplate } from "./local-importer"; @@ -185,7 +185,7 @@ const mergeConfigsAndTemplates = async ( }; const pipeline = async (value: InputConfigArrInstance, arrType: ArrType) => { - const api = getArrApi(); + const api = getUnifiedClient(); const { config, mergedCFs } = await mergeConfigsAndTemplates(value, arrType); @@ -240,7 +240,7 @@ const pipeline = async (value: InputConfigArrInstance, arrType: ArrType) => { logger.info("DryRun: Would update QualityDefinitions."); } else { logger.info(`Diffs in quality definitions found`, changeMap.values()); - await api.v3QualitydefinitionUpdateUpdate(restData as any); // TODO hack Ignore types + await api.updateQualityDefinitions(restData); // refresh QDs serverQD = await loadQualityDefinitionFromServer(); logger.info(`Updated QualityDefinitions`); @@ -269,7 +269,7 @@ const pipeline = async (value: InputConfigArrInstance, arrType: ArrType) => { if (!IS_DRY_RUN) { for (const element of create) { try { - const newProfile = await api.v3QualityprofileCreate(element as any); // Ignore types + const newProfile = await api.createQualityProfile(element); logger.info(`Created QualityProfile: ${newProfile.name}`); } catch (error: any) { logger.error(`Failed creating QualityProfile (${element.name})`); @@ -279,7 +279,7 @@ const pipeline = async (value: InputConfigArrInstance, arrType: ArrType) => { for (const element of changedQPs) { try { - const newProfile = await api.v3QualityprofileUpdate("" + element.id, element as any); // Ignore types + const newProfile = await api.updateQualityProfile("" + element.id, element); logger.info(`Updated QualityProfile: ${newProfile.name}`); } catch (error: any) { logger.error(`Failed updating QualityProfile (${element.name})`); diff --git a/src/quality-definitions.ts b/src/quality-definitions.ts index 650cbce..35ece83 100644 --- a/src/quality-definitions.ts +++ b/src/quality-definitions.ts @@ -1,6 +1,6 @@ import path from "node:path"; import { MergedQualityDefinitionResource } from "./__generated__/mergedTypes"; -import { getArrApi } from "./api"; +import { getUnifiedClient } from "./clients/unified-client"; import { logger } from "./logger"; import { TrashQualityDefintion, TrashQualityDefintionQuality } from "./types/trashguide.types"; import { IS_LOCAL_SAMPLE_MODE, loadJsonFile } from "./util"; @@ -9,7 +9,7 @@ export const loadQualityDefinitionFromServer = async (): Promise