diff --git a/src/actions/auth/status.ts b/src/actions/auth/status.ts index e77432bd..fc042abe 100644 --- a/src/actions/auth/status.ts +++ b/src/actions/auth/status.ts @@ -13,7 +13,11 @@ export class StatusAction { public async execute(authKey: string | null): Promise { const accountInfo = await getAuthInfo(this.configDir.toString()); - if (accountInfo === null) { + // `auth logout` blanks config.json rather than deleting it, so a logged-out user still + // has a non-null AuthInfo with an empty key. Checking the key catches both that and a + // missing config file, and avoids a request that can only come back 401. + if (!accountInfo?.authKey) { + this.prompts.notLoggedIn(); return ActionResult.failed(); } const result = await this.prompts.accountInfoSpinner( diff --git a/src/infrastructure/services/publishing-api-service.ts b/src/infrastructure/services/publishing-api-service.ts index e207c638..f03f0b55 100644 --- a/src/infrastructure/services/publishing-api-service.ts +++ b/src/infrastructure/services/publishing-api-service.ts @@ -24,8 +24,10 @@ export class PublishingApiService { shell: string ): Promise> { const authInfo: AuthInfo | null = await getAuthInfo(configDir.toString()); - if (authInfo === null) { - return err(ServiceError.UnAuthorized); + // `auth logout` blanks config.json rather than deleting it, so a logged-out user + // still has a non-null AuthInfo with an empty key — check the key, not the object. + if (!authInfo?.authKey) { + return err(ServiceError.unauthorizedWithHint(null)); } try { @@ -51,8 +53,10 @@ export class PublishingApiService { shell: string ): Promise> { const authInfo: AuthInfo | null = await getAuthInfo(configDir.toString()); - if (authInfo === null) { - return err(ServiceError.UnAuthorized); + // `auth logout` blanks config.json rather than deleting it, so a logged-out user + // still has a non-null AuthInfo with an empty key — check the key, not the object. + if (!authInfo?.authKey) { + return err(ServiceError.unauthorizedWithHint(null)); } const sdkFileStream = await this.fileService.getStream(sdkFilePath); @@ -107,8 +111,10 @@ export class PublishingApiService { shell: string ): Promise> { const authInfo: AuthInfo | null = await getAuthInfo(configDir.toString()); - if (authInfo === null) { - return err(ServiceError.UnAuthorized); + // `auth logout` blanks config.json rather than deleting it, so a logged-out user + // still has a non-null AuthInfo with an empty key — check the key, not the object. + if (!authInfo?.authKey) { + return err(ServiceError.unauthorizedWithHint(null)); } try { diff --git a/src/infrastructure/services/transformation-service.ts b/src/infrastructure/services/transformation-service.ts index 677aa186..119d4268 100644 --- a/src/infrastructure/services/transformation-service.ts +++ b/src/infrastructure/services/transformation-service.ts @@ -17,6 +17,7 @@ import { apiClientFactory } from "./api-client-factory.js"; import { FilePath } from "../../types/file/filePath.js"; import { CommandMetadata } from "../../types/common/command-metadata.js"; import { err, ok, Result} from "neverthrow"; +import { ServiceError } from "../service-error.js"; export interface TransformViaFileParams { file: FilePath; @@ -86,7 +87,7 @@ export class TransformationService { return "Your API Definition is invalid. Please use the APIMatic VS Code Extension to fix the errors and try again."; } else if (apiError.statusCode === 401) { const message = JSON.parse(apiError.body as string).message; - return `${message} You are not authorized to perform this action. Please run 'auth:login' or provide a valid auth key.`; + return ServiceError.unauthorizedWithHint(message).errorMessage; } return `Error ${apiError.statusCode}: An error occurred during the transformation. Please try again or contact support@apimatic.io for assistance.`; } else { diff --git a/src/infrastructure/services/validation-service.ts b/src/infrastructure/services/validation-service.ts index 8a20d753..462af9dd 100644 --- a/src/infrastructure/services/validation-service.ts +++ b/src/infrastructure/services/validation-service.ts @@ -247,7 +247,7 @@ export class ValidationService { case 400: return "Your API Definition is invalid. Please fix the issues and try again."; case 401: - return "You are not authorized to perform this action. Please run 'auth:login' or provide a valid auth key."; + return ServiceError.unauthorizedWithHint(null).errorMessage; case 403: return "You do not have permission to perform this action."; case 500: diff --git a/src/prompts/auth/status.ts b/src/prompts/auth/status.ts index 32fc48e6..5b3f9786 100644 --- a/src/prompts/auth/status.ts +++ b/src/prompts/auth/status.ts @@ -16,12 +16,17 @@ export class StatusPrompts { ); } + public notLoggedIn() { + log.error(`You are not logged in. Please run ${format.cmdAlt("apimatic", "auth", "login")} to log in.`); + } + public invalidKeyProvided(serviceError: ServiceError) { const message = serviceError === ServiceError.UnAuthorized ? "Invalid API key provided." : serviceError.errorMessage; log.error(message); } + public showAccountInfo(info: SubscriptionInfo) { const languages = mapLanguages(info.allowedLanguages); const message = `Account Information: