From 3ded2789e3b07e2a2ceebf23b7d39c286e266bd9 Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Wed, 12 Apr 2023 09:21:35 +0300
Subject: [PATCH] refactor(dotnet-version): Refactor schemas (#21426)

---
 .../datasource/dotnet-version/index.ts        | 89 +++++------------
 .../datasource/dotnet-version/schema.ts       | 95 ++++++++++---------
 2 files changed, 73 insertions(+), 111 deletions(-)

diff --git a/lib/modules/datasource/dotnet-version/index.ts b/lib/modules/datasource/dotnet-version/index.ts
index db8fce0494..a544e51754 100644
--- a/lib/modules/datasource/dotnet-version/index.ts
+++ b/lib/modules/datasource/dotnet-version/index.ts
@@ -1,14 +1,11 @@
-import is from '@sindresorhus/is';
 import { cache } from '../../../util/cache/package/decorator';
-import type { HttpResponse } from '../../../util/http/types';
+import * as p from '../../../util/promises';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
 import {
-  DotnetRelease,
-  DotnetReleases,
-  DotnetReleasesIndex,
-  DotnetReleasesIndexSchema,
-  DotnetReleasesSchema,
+  DotnetRuntimeReleases,
+  DotnetSdkReleases,
+  ReleasesIndex,
 } from './schema';
 
 export class DotnetVersionDatasource extends Datasource {
@@ -38,42 +35,29 @@ export class DotnetVersionDatasource extends Datasource {
       return null;
     }
 
-    let result: ReleaseResult | null = null;
-
-    let raw: HttpResponse<DotnetReleasesIndex> | null = null;
     try {
-      raw = await this.http.getJson(
-        this.defaultRegistryUrls[0],
-        DotnetReleasesIndexSchema
+      const registryUrl = this.defaultRegistryUrls[0];
+      const { body: urls } = await this.http.getJson(
+        registryUrl,
+        ReleasesIndex
       );
-    } catch (err) {
-      this.handleGenericErrors(err);
-    }
 
-    const body = raw?.body;
-    if (body) {
-      const releases: Release[] = [];
-      const { 'releases-index': releasesIndex } = body;
-
-      for (const { 'releases.json': releasesUrl } of releasesIndex) {
-        const channelReleases = await this.getChannelReleases(
-          releasesUrl,
-          packageName
-        );
-        if (channelReleases) {
-          releases.push(...channelReleases);
-        }
-      }
+      const channelReleases = await p.map(
+        urls,
+        (url) => this.getChannelReleases(url, packageName),
+        { concurrency: 1, stopOnError: true }
+      );
+      const releases = channelReleases.flat();
 
       const sourceUrl =
         packageName === 'dotnet-sdk'
           ? 'https://github.com/dotnet/sdk'
           : 'https://github.com/dotnet/runtime';
 
-      result = { releases, sourceUrl };
+      return { releases, sourceUrl };
+    } catch (err) {
+      this.handleGenericErrors(err);
     }
-
-    return result;
   }
 
   @cache({
@@ -85,43 +69,14 @@ export class DotnetVersionDatasource extends Datasource {
   async getChannelReleases(
     releaseUrl: string,
     packageName: string
-  ): Promise<Release[] | null> {
-    let result: Release[] = [];
-
-    let raw: HttpResponse<DotnetReleases> | null = null;
+  ): Promise<Release[]> {
+    const schema =
+      packageName === 'dotnet-sdk' ? DotnetSdkReleases : DotnetRuntimeReleases;
     try {
-      raw = await this.http.getJson(releaseUrl, DotnetReleasesSchema);
+      const { body } = await this.http.getJson(releaseUrl, schema);
+      return body;
     } catch (err) {
       this.handleGenericErrors(err);
     }
-
-    const body = raw?.body;
-    if (body) {
-      const type = DotnetVersionDatasource.getType(packageName);
-      const { releases: releases } = body;
-      result = releases
-        .filter(
-          (
-            release
-          ): release is {
-            [P in keyof DotnetRelease]: NonNullable<DotnetRelease[P]>;
-          } => {
-            return !is.nullOrUndefined(release[type]);
-          }
-        )
-        .map((release) => {
-          return {
-            version: release[type].version,
-            releaseTimestamp: release['release-date'],
-            changelogUrl: release['release-notes'],
-          };
-        });
-    }
-
-    return result;
-  }
-
-  private static getType(packageName: string): 'sdk' | 'runtime' {
-    return packageName === 'dotnet-sdk' ? 'sdk' : 'runtime';
   }
 }
diff --git a/lib/modules/datasource/dotnet-version/schema.ts b/lib/modules/datasource/dotnet-version/schema.ts
index 4f16fdb193..0985f96634 100644
--- a/lib/modules/datasource/dotnet-version/schema.ts
+++ b/lib/modules/datasource/dotnet-version/schema.ts
@@ -1,52 +1,59 @@
 import { z } from 'zod';
+import { looseArray } from '../../../util/schema-utils';
+import type { Release } from '../types';
 
-const Product = z.union([z.literal('.NET Core'), z.literal('.NET')]);
-const SupportPhase = z.union([
-  z.literal('current'),
-  z.literal('eol'),
-  z.literal('lts'),
-  z.literal('maintenance'),
-  z.literal('preview'),
-  z.literal('rc'),
-]);
-const ReleaseIndex = z.object({
-  'channel-version': z.string(),
-  'latest-release': z.string(),
-  'latest-release-date': z.string(),
-  security: z.boolean(),
-  'latest-runtime': z.string(),
-  'latest-sdk': z.string(),
-  product: Product,
-  'support-phase': SupportPhase,
-  'eol-date': z.string().nullable(),
-  'releases.json': z.string(),
-});
-export const DotnetReleasesIndexSchema = z.object({
-  'releases-index': z.array(ReleaseIndex),
-});
+export const ReleasesIndex = z
+  .object({
+    'releases-index': looseArray(
+      z
+        .object({
+          'releases.json': z.string(),
+        })
+        .transform(({ 'releases.json': releasesUrl }) => releasesUrl)
+    ),
+  })
+  .transform(({ 'releases-index': releasesIndex }) => releasesIndex);
 
-const ReleaseDetails = z.object({
-  version: z.string(),
-  'version-display': z.string(),
-});
-const ReleaseSchema = z.object({
+const ReleaseBase = z.object({
   'release-date': z.string(),
-  'release-version': z.string(),
-  security: z.boolean(),
   'release-notes': z.string(),
-  runtime: z.nullable(ReleaseDetails),
-  sdk: z.nullable(ReleaseDetails),
 });
-export const DotnetReleasesSchema = z.object({
-  'channel-version': z.string(),
-  'latest-release': z.string(),
-  'latest-release-date': z.string(),
-  'latest-runtime': z.string(),
-  'latest-sdk': z.string(),
-  'support-phase': SupportPhase,
-  releases: z.array(ReleaseSchema),
+const ReleaseDetails = z.object({
+  version: z.string(),
 });
 
-export type DotnetReleasesIndex = z.infer<typeof DotnetReleasesIndexSchema>;
-export type DotnetReleases = z.infer<typeof DotnetReleasesSchema>;
-export type DotnetRelease = z.infer<typeof ReleaseSchema>;
+export const DotnetSdkReleases = z
+  .object({
+    releases: looseArray(
+      ReleaseBase.extend({
+        sdk: ReleaseDetails,
+      })
+    ),
+  })
+  .transform(({ releases }): Release[] =>
+    releases.map(
+      ({
+        sdk: { version },
+        'release-date': releaseTimestamp,
+        'release-notes': changelogUrl,
+      }) => ({ version, releaseTimestamp, changelogUrl })
+    )
+  );
+
+export const DotnetRuntimeReleases = z
+  .object({
+    releases: looseArray(
+      ReleaseBase.extend({
+        runtime: ReleaseDetails,
+      })
+    ),
+  })
+  .transform(({ releases }): Release[] =>
+    releases.map(
+      ({
+        runtime: { version },
+        'release-date': releaseTimestamp,
+        'release-notes': changelogUrl,
+      }) => ({ version, releaseTimestamp, changelogUrl })
+    )
+  );
-- 
GitLab