From d10dcff495c7de06f324e0d26e6add6ee08747d6 Mon Sep 17 00:00:00 2001
From: Sebastian Poxhofer <secustor@users.noreply.github.com>
Date: Wed, 8 Nov 2023 07:05:22 +0100
Subject: [PATCH] refactor(custom/datasource): prepare for additional format
 types (#25640)

---
 .../datasource/custom/formats/plain.ts        | 23 ++++++++
 lib/modules/datasource/custom/index.ts        | 52 ++++---------------
 lib/modules/datasource/custom/utils.ts        | 18 +++++++
 lib/modules/platform/gitlab/index.ts          |  3 +-
 lib/util/http/index.ts                        | 10 ++++
 5 files changed, 61 insertions(+), 45 deletions(-)
 create mode 100644 lib/modules/datasource/custom/formats/plain.ts

diff --git a/lib/modules/datasource/custom/formats/plain.ts b/lib/modules/datasource/custom/formats/plain.ts
new file mode 100644
index 0000000000..ac1ef928e8
--- /dev/null
+++ b/lib/modules/datasource/custom/formats/plain.ts
@@ -0,0 +1,23 @@
+import type { Http } from '../../../../util/http';
+import { newlineRegex } from '../../../../util/regex';
+import type { ReleaseResult } from '../../types';
+
+export async function fetch(
+  http: Http,
+  url: string,
+): Promise<ReleaseResult | null> {
+  const response = await http.getPlain(url);
+  const contentType = response.headers['content-type'];
+  if (!contentType?.startsWith('text/')) {
+    return null;
+  }
+  const lines = response.body.split(newlineRegex).map((line) => line.trim());
+
+  const versions = lines.map((value) => {
+    return { version: value };
+  });
+
+  return {
+    releases: versions,
+  };
+}
diff --git a/lib/modules/datasource/custom/index.ts b/lib/modules/datasource/custom/index.ts
index 33438f9220..166e7958fe 100644
--- a/lib/modules/datasource/custom/index.ts
+++ b/lib/modules/datasource/custom/index.ts
@@ -1,11 +1,11 @@
 import is from '@sindresorhus/is';
 import jsonata from 'jsonata';
 import { logger } from '../../../logger';
-import { newlineRegex } from '../../../util/regex';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
+import { fetch as plainFetch } from './formats/plain';
 import { ReleaseResultZodSchema } from './schema';
-import { massageCustomDatasourceConfig } from './utils';
+import { getCustomConfig } from './utils';
 
 export class CustomDatasource extends Datasource {
   static readonly id = 'custom';
@@ -19,34 +19,20 @@ export class CustomDatasource extends Datasource {
   async getReleases(
     getReleasesConfig: GetReleasesConfig,
   ): Promise<ReleaseResult | null> {
-    const customDatasourceName = getReleasesConfig.datasource?.replace(
-      'custom.',
-      '',
-    );
-
-    if (!is.nonEmptyString(customDatasourceName)) {
-      logger.debug(
-        `No datasource has been supplied while looking up ${getReleasesConfig.packageName}`,
-      );
-      return null;
-    }
-
-    const config = massageCustomDatasourceConfig(
-      customDatasourceName,
-      getReleasesConfig,
-    );
+    const config = getCustomConfig(getReleasesConfig);
     if (is.nullOrUndefined(config)) {
       return null;
     }
 
     const { defaultRegistryUrlTemplate, transformTemplates, format } = config;
-    // TODO add here other format options than JSON and "plain"
     let response: unknown;
     try {
-      if (format === 'plain') {
-        response = await this.fetchPlainFormat(defaultRegistryUrlTemplate);
-      } else {
-        response = (await this.http.getJson(defaultRegistryUrlTemplate)).body;
+      switch (format) {
+        case 'plain':
+          response = await plainFetch(this.http, defaultRegistryUrlTemplate);
+          break;
+        case 'json':
+          response = (await this.http.getJson(defaultRegistryUrlTemplate)).body;
       }
     } catch (e) {
       this.handleHttpErrors(e);
@@ -69,24 +55,4 @@ export class CustomDatasource extends Datasource {
       return null;
     }
   }
-
-  private async fetchPlainFormat(url: string): Promise<unknown> {
-    const response = await this.http.get(url, {
-      headers: {
-        Accept: 'text/plain',
-      },
-    });
-    const contentType = response.headers['content-type'];
-    if (!contentType?.startsWith('text/')) {
-      return null;
-    }
-    const versions = response.body.split(newlineRegex).map((version) => {
-      return {
-        version: version.trim(),
-      };
-    });
-    return {
-      releases: versions,
-    };
-  }
 }
diff --git a/lib/modules/datasource/custom/utils.ts b/lib/modules/datasource/custom/utils.ts
index 15d95a0216..d011ca22b6 100644
--- a/lib/modules/datasource/custom/utils.ts
+++ b/lib/modules/datasource/custom/utils.ts
@@ -44,3 +44,21 @@ export function massageCustomDatasourceConfig(
     transformTemplates: transform,
   };
 }
+
+export function getCustomConfig(
+  getReleasesConfig: GetReleasesConfig,
+): Required<CustomDatasourceConfig> | null {
+  const customDatasourceName = getReleasesConfig.datasource?.replace(
+    'custom.',
+    '',
+  );
+
+  if (!is.nonEmptyString(customDatasourceName)) {
+    logger.debug(
+      `No datasource has been supplied while looking up ${getReleasesConfig.packageName}`,
+    );
+    return null;
+  }
+
+  return massageCustomDatasourceConfig(customDatasourceName, getReleasesConfig);
+}
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index 70db40a1c1..d85b4026f6 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -18,8 +18,7 @@ import {
 import { logger } from '../../../logger';
 import type { BranchStatus } from '../../../types';
 import { coerceArray } from '../../../util/array';
-import { noLeadingAtSymbol } from '../../../util/common';
-import { parseJson } from '../../../util/common';
+import { noLeadingAtSymbol, parseJson } from '../../../util/common';
 import * as git from '../../../util/git';
 import * as hostRules from '../../../util/host-rules';
 import { setBaseUrl } from '../../../util/http/gitlab';
diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts
index 98db134c62..f0dfd338a6 100644
--- a/lib/util/http/index.ts
+++ b/lib/util/http/index.ts
@@ -332,6 +332,16 @@ export class Http<Opts extends HttpOptions = HttpOptions> {
     return res;
   }
 
+  async getPlain(url: string, options?: Opts): Promise<HttpResponse> {
+    const opt = options ?? {};
+    return await this.get(url, {
+      headers: {
+        Accept: 'text/plain',
+      },
+      ...opt,
+    });
+  }
+
   getJson<ResT>(
     url: string,
     options?: Opts & HttpRequestOptions<ResT>,
-- 
GitLab