From fd3d577a8adb0f2c289d26f4e77ac025ce182dba Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Thu, 29 Jun 2023 19:22:26 +0300
Subject: [PATCH] feat: Schema utility for URL parsing (#23043)

---
 lib/util/schema-utils.spec.ts | 27 ++++++++++++++++++++++++++-
 lib/util/schema-utils.ts      |  9 +++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/lib/util/schema-utils.spec.ts b/lib/util/schema-utils.spec.ts
index f62fcc4bbf..9be7ac651c 100644
--- a/lib/util/schema-utils.spec.ts
+++ b/lib/util/schema-utils.spec.ts
@@ -1,5 +1,12 @@
 import { z } from 'zod';
-import { Json, Json5, LooseArray, LooseRecord, UtcDate } from './schema-utils';
+import {
+  Json,
+  Json5,
+  LooseArray,
+  LooseRecord,
+  Url,
+  UtcDate,
+} from './schema-utils';
 
 describe('util/schema-utils', () => {
   describe('LooseArray', () => {
@@ -270,4 +277,22 @@ describe('util/schema-utils', () => {
       expect(() => UtcDate.parse('foobar')).toThrow();
     });
   });
+
+  describe('Url', () => {
+    it('parses valid URLs', () => {
+      const urlStr = 'https://www.example.com/foo/bar?baz=qux';
+      const parsedUrl = Url.parse(urlStr);
+      expect(parsedUrl).toMatchObject({
+        protocol: 'https:',
+        hostname: 'www.example.com',
+        pathname: '/foo/bar',
+        search: '?baz=qux',
+      });
+    });
+
+    it('throws an error for invalid URLs', () => {
+      const urlStr = 'invalid-url-string';
+      expect(() => Url.parse(urlStr)).toThrow('Invalid URL');
+    });
+  });
 });
diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts
index f3dfe108ce..0caa342e39 100644
--- a/lib/util/schema-utils.ts
+++ b/lib/util/schema-utils.ts
@@ -223,3 +223,12 @@ export const UtcDate = z
     }
     return date;
   });
+
+export const Url = z.string().transform((str, ctx): URL => {
+  try {
+    return new URL(str);
+  } catch (e) {
+    ctx.addIssue({ code: 'custom', message: 'Invalid URL' });
+    return z.NEVER;
+  }
+});
-- 
GitLab