From 5e83d1422f927c7202cde0f57d47e790aeaf02c1 Mon Sep 17 00:00:00 2001
From: Robin Genz <mail@robingenz.dev>
Date: Fri, 1 Nov 2024 06:46:43 +0100
Subject: [PATCH] feat(apps): add new `--expires-in-days` option to
 `apps:bundles:create`

---
 src/commands/apps/bundles/create.ts | 48 +++++++++++------------------
 src/services/app-bundles.ts         |  3 ++
 src/types/app-bundle.ts             |  1 +
 3 files changed, 22 insertions(+), 30 deletions(-)

diff --git a/src/commands/apps/bundles/create.ts b/src/commands/apps/bundles/create.ts
index fd89d0a..b3bbd6f 100644
--- a/src/commands/apps/bundles/create.ts
+++ b/src/commands/apps/bundles/create.ts
@@ -1,6 +1,5 @@
 import { defineCommand } from 'citty';
 import consola from 'consola';
-import FormData from 'form-data';
 import { createReadStream } from 'fs';
 import appBundleFilesService from '../../../services/app-bundle-files';
 import appBundlesService from '../../../services/app-bundles';
@@ -41,6 +40,10 @@ export default defineCommand({
       type: 'string',
       description: 'Channel to associate the bundle with.',
     },
+    expiresInDays: {
+      type: 'string',
+      description: 'The number of days until the bundle is automatically deleted.',
+    },
     iosMax: {
       type: 'string',
       description: 'The maximum iOS bundle version (`CFBundleVersion`) that the bundle supports.',
@@ -80,12 +83,25 @@ export default defineCommand({
         ? ctx.args.artifactType
         : ('zip' as 'manifest' | 'zip');
     let channelName = ctx.args.channel as string | undefined;
+    let expiresInDays = ctx.args.expiresInDays as string | undefined;
     let iosMax = ctx.args.iosMax as string | undefined;
     let iosMin = ctx.args.iosMin as string | undefined;
     let path = ctx.args.path as string | undefined;
     let privateKey = ctx.args.privateKey as string | undefined;
     let rollout = ctx.args.rollout as string | undefined;
     let url = ctx.args.url as string | undefined;
+    // Validate the expiration days
+    let expiresAt: string | undefined;
+    if (expiresInDays) {
+      const expiresInDaysAsNumber = parseInt(expiresInDays, 10);
+      if (isNaN(expiresInDaysAsNumber) || expiresInDaysAsNumber < 1) {
+        consola.error('Expires in days must be a number greater than 0.');
+        return;
+      }
+      const expiresAtDate = new Date();
+      expiresAtDate.setDate(expiresAtDate.getDate() + expiresInDaysAsNumber);
+      expiresAt = expiresAtDate.toISOString();
+    }
     if (!path && !url) {
       path = await prompt('Enter the path to the app bundle:', {
         type: 'text',
@@ -155,35 +171,6 @@ export default defineCommand({
       }
     }
 
-    // Create form data
-    const formData = new FormData();
-    formData.append('artifactType', artifactType || 'zip');
-    if (url) {
-      formData.append('url', url);
-    }
-    if (channelName) {
-      formData.append('channelName', channelName);
-    }
-    if (androidMax) {
-      formData.append('maxAndroidAppVersionCode', androidMax);
-    }
-    if (androidMin) {
-      formData.append('minAndroidAppVersionCode', androidMin);
-    }
-    if (rollout) {
-      const rolloutAsNumber = parseFloat(rollout);
-      if (isNaN(rolloutAsNumber) || rolloutAsNumber < 0 || rolloutAsNumber > 1) {
-        consola.error('Rollout percentage must be a number between 0 and 1 (e.g. 0.5).');
-        return;
-      }
-      formData.append('rolloutPercentage', rolloutAsNumber);
-    }
-    if (iosMax) {
-      formData.append('maxIosAppVersionCode', iosMax);
-    }
-    if (iosMin) {
-      formData.append('minIosAppVersionCode', iosMin);
-    }
     let appBundleId: string | undefined;
     try {
       // Create the app bundle
@@ -192,6 +179,7 @@ export default defineCommand({
         appId,
         artifactType,
         channelName,
+        expiresAt: expiresAt,
         url,
         maxAndroidAppVersionCode: androidMax,
         maxIosAppVersionCode: iosMax,
diff --git a/src/services/app-bundles.ts b/src/services/app-bundles.ts
index 94f22d5..b38a929 100644
--- a/src/services/app-bundles.ts
+++ b/src/services/app-bundles.ts
@@ -22,6 +22,9 @@ class AppBundlesServiceImpl implements AppBundlesService {
     if (dto.channelName) {
       formData.append('channelName', dto.channelName);
     }
+    if (dto.expiresAt) {
+      formData.append('expiresAt', dto.expiresAt);
+    }
     if (dto.url) {
       formData.append('url', dto.url);
     }
diff --git a/src/types/app-bundle.ts b/src/types/app-bundle.ts
index 21bf4d4..fbccae0 100644
--- a/src/types/app-bundle.ts
+++ b/src/types/app-bundle.ts
@@ -6,6 +6,7 @@ export interface CreateAppBundleDto {
   appId: string;
   artifactType: 'manifest' | 'zip';
   channelName?: string;
+  expiresAt?: string;
   url?: string;
   maxAndroidAppVersionCode?: string;
   maxIosAppVersionCode?: string;