From 3378ae7e3985909f1bbeae69ab712e4e9d21b9d7 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <84702959+andrii-balitskyi@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:49:54 +0100 Subject: [PATCH] Add loading state while seam API is requested (#242) --- lib/interact-for-access-code.ts | 9 +- lib/interact-for-acs-entrance.ts | 5 +- lib/interact-for-acs-system.ts | 5 +- lib/interact-for-acs-user.ts | 9 +- lib/interact-for-action-attempt-poll.ts | 11 +- lib/interact-for-connected-account.ts | 6 +- lib/interact-for-credential-pool.ts | 11 +- lib/interact-for-device.ts | 5 +- lib/interact-for-user-identity.ts | 5 +- lib/interact-for-workspace-id.ts | 5 +- lib/util/request-seam-api.ts | 9 +- lib/util/with-loading.ts | 16 ++ package-lock.json | 218 +++++++++++++++++++++++- package.json | 1 + 14 files changed, 292 insertions(+), 23 deletions(-) create mode 100644 lib/util/with-loading.ts diff --git a/lib/interact-for-access-code.ts b/lib/interact-for-access-code.ts index 04ffa81..fca4dac 100644 --- a/lib/interact-for-access-code.ts +++ b/lib/interact-for-access-code.ts @@ -1,6 +1,7 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { interactForDevice } from "./interact-for-device" +import { withLoading } from "./util/with-loading" export const interactForAccessCode = async ({ device_id, @@ -13,9 +14,11 @@ export const interactForAccessCode = async ({ device_id = await interactForDevice() } - const accessCodes = await seam.accessCodes.list({ - device_id, - }) + const accessCodes = await withLoading("Fetching access codes...", () => + seam.accessCodes.list({ + device_id, + }) + ) const { accessCodeId } = await prompts({ name: "accessCodeId", diff --git a/lib/interact-for-acs-entrance.ts b/lib/interact-for-acs-entrance.ts index 6f54b34..c01498c 100644 --- a/lib/interact-for-acs-entrance.ts +++ b/lib/interact-for-acs-entrance.ts @@ -1,10 +1,13 @@ import prompts from "prompts" import { getSeam } from "./get-seam" +import { withLoading } from "./util/with-loading" export const interactForAcsEntrance = async () => { const seam = await getSeam() - const entrances = await seam.acs.entrances.list() + const entrances = await withLoading("Fetching ACS entrances...", () => + seam.acs.entrances.list() + ) const { acsEntranceId } = await prompts({ name: "acsEntranceId", diff --git a/lib/interact-for-acs-system.ts b/lib/interact-for-acs-system.ts index 7decac2..6668f84 100644 --- a/lib/interact-for-acs-system.ts +++ b/lib/interact-for-acs-system.ts @@ -1,10 +1,13 @@ import prompts from "prompts" import { getSeam } from "./get-seam" +import { withLoading } from "./util/with-loading" export const interactForAcsSystem = async (message?: string) => { const seam = await getSeam() - const systems = await seam.acs.systems.list() + const systems = await withLoading("Fetching ACS systems...", () => + seam.acs.systems.list() + ) const { acsSystemId } = await prompts({ name: "acsSystemId", diff --git a/lib/interact-for-acs-user.ts b/lib/interact-for-acs-user.ts index 1682aa5..1b5e68f 100644 --- a/lib/interact-for-acs-user.ts +++ b/lib/interact-for-acs-user.ts @@ -1,6 +1,7 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { interactForAcsSystem } from "./interact-for-acs-system" +import { withLoading } from "./util/with-loading" export const interactForAcsUser = async () => { const seam = await getSeam() @@ -9,9 +10,11 @@ export const interactForAcsUser = async () => { "What acs_system does the acs_user belong to?" ) - const users = await seam.acs.users.list({ - acs_system_id, - }) + const users = await withLoading("Fetching ACS users...", () => + seam.acs.users.list({ + acs_system_id, + }) + ) const { acsUserId } = await prompts({ name: "acsUserId", diff --git a/lib/interact-for-action-attempt-poll.ts b/lib/interact-for-action-attempt-poll.ts index 339e1bf..800a696 100644 --- a/lib/interact-for-action-attempt-poll.ts +++ b/lib/interact-for-action-attempt-poll.ts @@ -1,6 +1,7 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { ActionAttemptsGetResponse } from "@seamapi/http/connect" +import { withLoading } from "./util/with-loading" export const interactForActionAttemptPoll = async ( action_attempt: ActionAttemptsGetResponse["action_attempt"] @@ -19,9 +20,13 @@ export const interactForActionAttemptPoll = async ( const seam = await getSeam() const { action_attempt_id } = action_attempt - const updated_action_attempt = await seam.actionAttempts.get( - { action_attempt_id }, - { waitForActionAttempt: { pollingInterval: 240, timeout: 10_000 } } + const updated_action_attempt = await withLoading( + "Polling action attempt...", + () => + seam.actionAttempts.get( + { action_attempt_id }, + { waitForActionAttempt: { pollingInterval: 240, timeout: 10_000 } } + ) ) console.dir(updated_action_attempt, { depth: null }) diff --git a/lib/interact-for-connected-account.ts b/lib/interact-for-connected-account.ts index 7005fc7..dc938ed 100644 --- a/lib/interact-for-connected-account.ts +++ b/lib/interact-for-connected-account.ts @@ -1,10 +1,14 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { getConfigStore } from "./get-config-store" +import { withLoading } from "./util/with-loading" export const interactForConnectedAccount = async () => { const seam = await getSeam() - const connected_accounts = await seam.connectedAccounts.list() + const connected_accounts = await withLoading( + "Fetching connected accounts...", + () => seam.connectedAccounts.list() + ) const { connectedAccountId } = await prompts({ name: "connectedAccountId", diff --git a/lib/interact-for-credential-pool.ts b/lib/interact-for-credential-pool.ts index fbca57f..7b3d821 100644 --- a/lib/interact-for-credential-pool.ts +++ b/lib/interact-for-credential-pool.ts @@ -1,6 +1,7 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { interactForAcsSystem } from "./interact-for-acs-system" +import { withLoading } from "./util/with-loading" export const interactForCredentialPool = async () => { const seam = await getSeam() @@ -9,9 +10,13 @@ export const interactForCredentialPool = async () => { "What acs_system does the credential pool belong to?" ) - const credentialPools = await seam.acs.credentialPools.list({ - acs_system_id, - }) + const credentialPools = await withLoading( + "Fetching ACS credential pools...", + () => + seam.acs.credentialPools.list({ + acs_system_id, + }) + ) const { credentialPoolId } = await prompts({ name: "credentialPoolId", diff --git a/lib/interact-for-device.ts b/lib/interact-for-device.ts index 693f502..3c584ae 100644 --- a/lib/interact-for-device.ts +++ b/lib/interact-for-device.ts @@ -1,10 +1,13 @@ import prompts from "prompts" import { getSeam } from "./get-seam" import { getConfigStore } from "./get-config-store" +import { withLoading } from "./util/with-loading" export const interactForDevice = async () => { const seam = await getSeam() - const devices = await seam.devices.list() + const devices = await withLoading("Fetching devices...", () => + seam.devices.list() + ) const { deviceId } = await prompts({ name: "deviceId", diff --git a/lib/interact-for-user-identity.ts b/lib/interact-for-user-identity.ts index 1657496..a6a31e4 100644 --- a/lib/interact-for-user-identity.ts +++ b/lib/interact-for-user-identity.ts @@ -1,10 +1,13 @@ import prompts from "prompts" import { getSeam } from "./get-seam" +import { withLoading } from "./util/with-loading" export const interactForUserIdentity = async () => { const seam = await getSeam() - const uis = await seam.userIdentities.list() + const uis = await withLoading("Fetching user identities...", () => + seam.userIdentities.list() + ) const { userIdentityId } = await prompts({ name: "userIdentityId", diff --git a/lib/interact-for-workspace-id.ts b/lib/interact-for-workspace-id.ts index 4d4a54c..6a0da5f 100644 --- a/lib/interact-for-workspace-id.ts +++ b/lib/interact-for-workspace-id.ts @@ -1,12 +1,15 @@ import { getConfigStore } from "./get-config-store" import prompts from "prompts" import { getSeam, getSeamMultiWorkspace } from "./get-seam" +import { withLoading } from "./util/with-loading" export const interactForWorkspaceId = async () => { const config = getConfigStore() const seam = await getSeamMultiWorkspace() - const workspaces = await seam.workspaces.list() + const workspaces = await withLoading("Fetching workspaces...", () => + seam.workspaces.list() + ) const { workspaceId } = await prompts({ name: "workspaceId", diff --git a/lib/util/request-seam-api.ts b/lib/util/request-seam-api.ts index bac76c1..192cfcc 100644 --- a/lib/util/request-seam-api.ts +++ b/lib/util/request-seam-api.ts @@ -1,6 +1,7 @@ import type { AxiosResponse } from "axios" import chalk from "chalk" import { getSeam } from "../get-seam" +import { withLoading } from "./with-loading" export const RequestSeamApi = async ({ path, @@ -13,9 +14,11 @@ export const RequestSeamApi = async ({ logRequest(path, params) - const response = await seam.client.post(path, params, { - validateStatus: () => true, - }) + const response = await withLoading("Making request...", () => + seam.client.post(path, params, { + validateStatus: () => true, + }) + ) logResponse(response) diff --git a/lib/util/with-loading.ts b/lib/util/with-loading.ts new file mode 100644 index 0000000..da91dba --- /dev/null +++ b/lib/util/with-loading.ts @@ -0,0 +1,16 @@ +import ora from "ora" + +export const withLoading = async ( + message: string, + fn: () => Promise +): Promise => { + const spinner = ora(message).start() + try { + const result = await fn() + spinner.succeed() + return result + } catch (error) { + spinner.fail() + throw error + } +} diff --git a/package-lock.json b/package-lock.json index 9b45a84..f201a31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@seamapi/http": "^0.34.1", "@seamapi/types": "^1.287.3", "open": "^10.0.3", + "ora": "^8.1.0", "swagger-parser": "^10.0.3" }, "bin": { @@ -27,7 +28,7 @@ "@types/prompts": "^2.4.9", "@vercel/ncc": "^0.38.1", "axios": "^1.6.2", - "bun-types": "*", + "bun-types": "latest", "chalk": "^5.3.0", "command-line-usage": "^7.0.1", "configstore": "^6.0.0", @@ -1350,7 +1351,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -1422,6 +1422,31 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -1971,6 +1996,17 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -2272,6 +2308,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2319,6 +2366,17 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -2455,6 +2513,32 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2523,6 +2607,17 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -2747,6 +2842,74 @@ "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" }, + "node_modules/ora": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz", + "integrity": "sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/p-is-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", @@ -3139,6 +3302,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3331,6 +3534,17 @@ "node": ">= 8" } }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stream-meter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", diff --git a/package.json b/package.json index 1ece3eb..2110573 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@seamapi/http": "^0.34.1", "@seamapi/types": "^1.287.3", "open": "^10.0.3", + "ora": "^8.1.0", "swagger-parser": "^10.0.3" } }