From 6c8c469ca03ef9a9d393b3c20d4e2aaa09201472 Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Mon, 21 Aug 2023 18:29:41 +0300
Subject: [PATCH] refactor(schema): Use `Json` helper schema instead of
 `JSON.parse` (#23997)

---
 lib/config/decrypt.ts                      |  4 +--
 lib/config/schema.ts                       | 13 +++++----
 lib/modules/manager/pipenv/artifacts.ts    |  8 ++----
 lib/modules/manager/pipenv/schema.ts       | 32 ++++++++++++----------
 lib/modules/platform/azure/azure-helper.ts |  4 +--
 lib/modules/platform/azure/schema.ts       | 28 ++++++++++---------
 lib/util/cache/repository/impl/base.ts     |  9 +++---
 lib/util/cache/repository/schema.ts        | 21 ++++++++------
 lib/util/emoji.ts                          | 10 +++----
 9 files changed, 65 insertions(+), 64 deletions(-)

diff --git a/lib/config/decrypt.ts b/lib/config/decrypt.ts
index 80fb62b524..533d255802 100644
--- a/lib/config/decrypt.ts
+++ b/lib/config/decrypt.ts
@@ -94,9 +94,7 @@ export async function tryDecrypt(
     const decryptedObjStr = await tryDecryptPgp(privateKey, encryptedStr);
     if (decryptedObjStr) {
       try {
-        const decryptedObj = DecryptedObject.safeParse(
-          JSON.parse(decryptedObjStr)
-        );
+        const decryptedObj = DecryptedObject.safeParse(decryptedObjStr);
         // istanbul ignore if
         if (!decryptedObj.success) {
           const error = new Error('config-validation');
diff --git a/lib/config/schema.ts b/lib/config/schema.ts
index 7a2661c369..3541b34c95 100644
--- a/lib/config/schema.ts
+++ b/lib/config/schema.ts
@@ -1,7 +1,10 @@
 import { z } from 'zod';
+import { Json } from '../util/schema-utils';
 
-export const DecryptedObject = z.object({
-  o: z.string().optional(),
-  r: z.string().optional(),
-  v: z.string().optional(),
-});
+export const DecryptedObject = Json.pipe(
+  z.object({
+    o: z.string().optional(),
+    r: z.string().optional(),
+    v: z.string().optional(),
+  })
+);
diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts
index d2f3ea3363..db98b3df96 100644
--- a/lib/modules/manager/pipenv/artifacts.ts
+++ b/lib/modules/manager/pipenv/artifacts.ts
@@ -28,9 +28,7 @@ export function getPythonConstraint(
     return python;
   }
   try {
-    const result = PipfileLockSchema.safeParse(
-      JSON.parse(existingLockFileContent)
-    );
+    const result = PipfileLockSchema.safeParse(existingLockFileContent);
     // istanbul ignore if: not easily testable
     if (!result.success) {
       logger.warn({ error: result.error }, 'Invalid Pipfile.lock');
@@ -62,9 +60,7 @@ export function getPipenvConstraint(
     return pipenv;
   }
   try {
-    const result = PipfileLockSchema.safeParse(
-      JSON.parse(existingLockFileContent)
-    );
+    const result = PipfileLockSchema.safeParse(existingLockFileContent);
     // istanbul ignore if: not easily testable
     if (!result.success) {
       logger.warn({ error: result.error }, 'Invalid Pipfile.lock');
diff --git a/lib/modules/manager/pipenv/schema.ts b/lib/modules/manager/pipenv/schema.ts
index a57cac2148..e39a9b112c 100644
--- a/lib/modules/manager/pipenv/schema.ts
+++ b/lib/modules/manager/pipenv/schema.ts
@@ -1,4 +1,5 @@
 import { z } from 'zod';
+import { Json } from '../../../util/schema-utils';
 
 const PipfileLockEntrySchema = z
   .record(
@@ -8,17 +9,20 @@ const PipfileLockEntrySchema = z
     })
   )
   .optional();
-export const PipfileLockSchema = z.object({
-  _meta: z
-    .object({
-      requires: z
-        .object({
-          python_version: z.string().optional(),
-          python_full_version: z.string().optional(),
-        })
-        .optional(),
-    })
-    .optional(),
-  default: PipfileLockEntrySchema,
-  develop: PipfileLockEntrySchema,
-});
+
+export const PipfileLockSchema = Json.pipe(
+  z.object({
+    _meta: z
+      .object({
+        requires: z
+          .object({
+            python_version: z.string().optional(),
+            python_full_version: z.string().optional(),
+          })
+          .optional(),
+      })
+      .optional(),
+    default: PipfileLockEntrySchema,
+    develop: PipfileLockEntrySchema,
+  })
+);
diff --git a/lib/modules/platform/azure/azure-helper.ts b/lib/modules/platform/azure/azure-helper.ts
index 55233bc61d..8273d9e04d 100644
--- a/lib/modules/platform/azure/azure-helper.ts
+++ b/lib/modules/platform/azure/azure-helper.ts
@@ -83,9 +83,7 @@ export async function getFile(
   if (item?.readable) {
     const fileContent = await streamToString(item);
     try {
-      const result = await WrappedExceptionSchema.safeParseAsync(
-        JSON.parse(fileContent)
-      );
+      const result = WrappedExceptionSchema.safeParse(fileContent);
       if (result.success) {
         if (result.data.typeKey === 'GitItemNotFoundException') {
           logger.warn(`Unable to find file ${filePath}`);
diff --git a/lib/modules/platform/azure/schema.ts b/lib/modules/platform/azure/schema.ts
index f305ee2ee7..57406229d1 100644
--- a/lib/modules/platform/azure/schema.ts
+++ b/lib/modules/platform/azure/schema.ts
@@ -1,17 +1,19 @@
 import type { WrappedException } from 'azure-devops-node-api/interfaces/common/VSSInterfaces';
 import { z } from 'zod';
+import { Json } from '../../../util/schema-utils';
 
-export const WrappedExceptionSchema: z.ZodSchema<WrappedException> = z.lazy(
-  () =>
-    z.object({
-      customProperties: z.record(z.any()).optional(),
-      errorCode: z.number().optional(),
-      eventId: z.number().optional(),
-      helpLink: z.string().optional(),
-      innerException: WrappedExceptionSchema.optional(),
-      message: z.string().optional(),
-      stackTrace: z.string().optional(),
-      typeKey: z.string().optional(),
-      typeName: z.string().optional(),
-    })
+const WrappedException: z.ZodSchema<WrappedException> = z.lazy(() =>
+  z.object({
+    customProperties: z.record(z.any()).optional(),
+    errorCode: z.number().optional(),
+    eventId: z.number().optional(),
+    helpLink: z.string().optional(),
+    innerException: WrappedException.optional(),
+    message: z.string().optional(),
+    stackTrace: z.string().optional(),
+    typeKey: z.string().optional(),
+    typeName: z.string().optional(),
+  })
 );
+
+export const WrappedExceptionSchema = Json.pipe(WrappedException);
diff --git a/lib/util/cache/repository/impl/base.ts b/lib/util/cache/repository/impl/base.ts
index 878fcad50b..3025e3ee9b 100644
--- a/lib/util/cache/repository/impl/base.ts
+++ b/lib/util/cache/repository/impl/base.ts
@@ -48,14 +48,13 @@ export abstract class RepoCacheBase implements RepoCache {
 
   async load(): Promise<void> {
     try {
-      const rawOldCache = await this.read();
-      if (!is.string(rawOldCache)) {
+      const oldCache = await this.read();
+      if (!is.string(oldCache)) {
         logger.debug(
-          `RepoCacheBase.load() - expecting data of type 'string' received '${typeof rawOldCache}' instead - skipping`
+          `RepoCacheBase.load() - expecting data of type 'string' received '${typeof oldCache}' instead - skipping`
         );
         return;
       }
-      const oldCache = JSON.parse(rawOldCache) as unknown;
 
       const cacheV13 = RepoCacheV13.safeParse(oldCache);
       if (cacheV13.success) {
@@ -65,7 +64,7 @@ export abstract class RepoCacheBase implements RepoCache {
       }
 
       logger.debug('Repository cache is invalid');
-    } catch (err) {
+    } catch (err) /* istanbul ignore next: not easily testable */ {
       logger.debug({ err }, 'Error reading repository cache');
     }
   }
diff --git a/lib/util/cache/repository/schema.ts b/lib/util/cache/repository/schema.ts
index 61af6926e0..5a337b0c6b 100644
--- a/lib/util/cache/repository/schema.ts
+++ b/lib/util/cache/repository/schema.ts
@@ -1,13 +1,16 @@
 import { z } from 'zod';
+import { Json } from '../../schema-utils';
 
-export const RepoCacheV13 = z
-  .object({
-    repository: z.string().min(1),
-    revision: z.number().refine((v) => v === 13),
-    payload: z.string().min(1),
-    hash: z.string().min(1),
-    fingerprint: z.string().min(1),
-  })
-  .strict();
+export const RepoCacheV13 = Json.pipe(
+  z
+    .object({
+      repository: z.string().min(1),
+      revision: z.number().refine((v) => v === 13),
+      payload: z.string().min(1),
+      hash: z.string().min(1),
+      fingerprint: z.string().min(1),
+    })
+    .strict()
+);
 
 export type RepoCacheRecord = z.infer<typeof RepoCacheV13>;
diff --git a/lib/util/emoji.ts b/lib/util/emoji.ts
index 9fe537dc05..0e8dff8788 100644
--- a/lib/util/emoji.ts
+++ b/lib/util/emoji.ts
@@ -12,6 +12,7 @@ import type { RenovateConfig } from '../config/types';
 import dataFiles from '../data-files.generated';
 import { logger } from '../logger';
 import { regEx } from './regex';
+import { Json } from './schema-utils';
 
 let unicodeEmoji = true;
 
@@ -19,17 +20,14 @@ let mappingsInitialized = false;
 const shortCodesByHex = new Map<string, string>();
 const hexCodesByShort = new Map<string, string>();
 
-const EmojiShortcodesSchema = z.record(
-  z.string(),
-  z.union([z.string(), z.array(z.string())])
+const EmojiShortcodesSchema = Json.pipe(
+  z.record(z.string(), z.union([z.string(), z.array(z.string())]))
 );
 
 function lazyInitMappings(): void {
   if (!mappingsInitialized) {
     const result = EmojiShortcodesSchema.safeParse(
-      JSON.parse(
-        dataFiles.get('node_modules/emojibase-data/en/shortcodes/github.json')!
-      )
+      dataFiles.get('node_modules/emojibase-data/en/shortcodes/github.json')!
     );
     // istanbul ignore if: not easily testable
     if (!result.success) {
-- 
GitLab