diff --git a/src/common/format.test.ts b/src/common/format.test.ts index 44ed27ff..9cc0fdb3 100644 --- a/src/common/format.test.ts +++ b/src/common/format.test.ts @@ -48,9 +48,7 @@ describe('Format utils', () => { }); it('formats path to app root from Superface directory', () => { - expect(formatPath(APP_PATH, SUPERFACE_DIR_PATH)).toEqual( - '..' - ); + expect(formatPath(APP_PATH, SUPERFACE_DIR_PATH)).toEqual('..'); }); }); }); diff --git a/src/common/polling.ts b/src/common/polling.ts index 63ad9a44..d5b29984 100644 --- a/src/common/polling.ts +++ b/src/common/polling.ts @@ -4,7 +4,7 @@ import type { UserError } from './error'; import type { UX } from './ux'; export const DEFAULT_POLLING_TIMEOUT_SECONDS = 300; -export const DEFAULT_POLLING_INTERVAL_SECONDS = 1; +export const DEFAULT_POLLING_INTERVAL_SECONDS = 2; enum PollStatus { Success = 'Success', @@ -94,12 +94,10 @@ export async function pollUrl( }; }, { - // logger, client, userError, ux, }: { - // logger: ILogger; client: ServiceClient; userError: UserError; ux: UX; @@ -111,6 +109,8 @@ export async function pollUrl( const pollingIntervalMilliseconds = (options?.pollingIntervalSeconds ?? DEFAULT_POLLING_INTERVAL_SECONDS) * 1000; + + let lastEvenetDescription = ''; while ( new Date().getTime() - startPollingTimeStamp.getTime() < timeoutMilliseconds @@ -118,6 +118,8 @@ export async function pollUrl( const result = await pollFetch(url, { client, userError }); if (result.status === PollStatus.Success) { + ux.succeed(`Successfully finished operation`); + return result.result_url; } else if (result.status === PollStatus.Failed) { throw userError( @@ -133,19 +135,23 @@ export async function pollUrl( )}: Operation has been cancelled.`, 1 ); + } else if (result.status === PollStatus.Pending) { + // get events from response and present them to user + if (result.events.length > 0 && options?.quiet !== true) { + const currentEvent = result.events[result.events.length - 1]; + + if (currentEvent.description !== lastEvenetDescription) { + // console.log(`${currentEvent.type} - ${currentEvent.description}`); + ux.info(`${currentEvent.type} - ${currentEvent.description}`); + } + + lastEvenetDescription = currentEvent.description; + + await new Promise(resolve => + setTimeout(resolve, pollingIntervalMilliseconds) + ); + } } - - // get events from response and present them to user - if (result.events.length > 0 && options?.quiet !== true) { - const lastEvent = result.events[result.events.length - 1]; - - ux.info(`${lastEvent.type} - ${lastEvent.description}`); - // logger.info('pollingEvent', lastEvent.type, lastEvent.description); - } - - await new Promise(resolve => - setTimeout(resolve, pollingIntervalMilliseconds) - ); } throw userError( @@ -179,7 +185,19 @@ async function pollFetch( }, }); if (result.status === 200) { - const data = (await result.json()) as unknown; + let data: unknown; + try { + data = (await result.json()) as unknown; + } catch (error) { + throw userError( + `Unexpected response from server: ${JSON.stringify( + await result.text(), + null, + 2 + )}`, + 1 + ); + } if (isPollResponse(data)) { return data; diff --git a/src/common/ux.ts b/src/common/ux.ts index a94690af..6e1ac2d3 100644 --- a/src/common/ux.ts +++ b/src/common/ux.ts @@ -14,7 +14,7 @@ export class UX { private lastText = ''; private constructor() { - this.spinner = createSpinner(undefined, { color: 'cyan', interval: 25 }); + this.spinner = createSpinner(undefined, { color: 'cyan', interval: 50 }); UX.instance = this; } @@ -32,7 +32,8 @@ export class UX { } public info(text: string): void { - if (text !== this.lastText) { + if (text.trim() !== this.lastText.trim()) { + this.spinner.clear(); this.spinner.update({ text }); } @@ -46,6 +47,10 @@ export class UX { }); } + public stop(): void { + this.spinner.stop(); + } + public static create(): UX { if (UX.instance === undefined) { UX.instance = new UX(); @@ -55,6 +60,7 @@ export class UX { } public static clear(): void { + UX.instance?.spinner.clear(); UX.instance?.spinner.stop(); } } diff --git a/src/logic/application-code/dotenv/onesdk-token.ts b/src/logic/application-code/dotenv/onesdk-token.ts index 3babd854..017944b4 100644 --- a/src/logic/application-code/dotenv/onesdk-token.ts +++ b/src/logic/application-code/dotenv/onesdk-token.ts @@ -1,3 +1,5 @@ export const ONESDK_TOKEN_ENV = 'SUPERFACE_ONESDK_TOKEN'; -export const ONESDK_TOKEN_COMMENT = 'The token for monitoring your Comlinks at https://superface.ai' -export const ONESDK_TOKEN_UNAVAILABLE_COMMENT = 'Set your OneSDK token to monitor your usage out-of-the-box. Get yours at https://superface.ai' \ No newline at end of file +export const ONESDK_TOKEN_COMMENT = + 'The token for monitoring your Comlinks at https://superface.ai'; +export const ONESDK_TOKEN_UNAVAILABLE_COMMENT = + 'Set your OneSDK token to monitor your usage out-of-the-box. Get yours at https://superface.ai'; diff --git a/src/logic/map.ts b/src/logic/map.ts index ce4de44b..2bf31b6a 100644 --- a/src/logic/map.ts +++ b/src/logic/map.ts @@ -11,7 +11,8 @@ export type MapPreparationResponse = { }; function assertMapResponse( - input: unknown + input: unknown, + { userError }: { userError: UserError } ): asserts input is MapPreparationResponse { if (typeof input === 'object' && input !== null && 'source' in input) { const tmp = input as { source: string }; @@ -21,7 +22,7 @@ function assertMapResponse( } } - throw Error(`Unexpected response received`); + throw userError(`Unexpected response received`, 1); } export async function mapProviderToProfile( @@ -58,6 +59,7 @@ export async function mapProviderToProfile( return ( await finishMapPreparation(resultUrl, { client, + userError, }) ).source; } @@ -127,7 +129,7 @@ async function startMapPreparation( async function finishMapPreparation( resultUrl: string, - { client }: { client: ServiceClient } + { client, userError }: { client: ServiceClient; userError: UserError } ): Promise { const resultResponse = await client.fetch(resultUrl, { method: 'GET', @@ -139,12 +141,15 @@ async function finishMapPreparation( }); if (resultResponse.status !== 200) { - throw Error(`Unexpected status code ${resultResponse.status} received`); + throw userError( + `Unexpected status code ${resultResponse.status} received`, + 1 + ); } const body = (await resultResponse.json()) as unknown; - assertMapResponse(body); + assertMapResponse(body, { userError }); return body; } diff --git a/src/logic/new.ts b/src/logic/new.ts index 6b312616..dc5fa250 100644 --- a/src/logic/new.ts +++ b/src/logic/new.ts @@ -1,5 +1,5 @@ import type { ProviderJson } from '@superfaceai/ast'; -import { parseDocumentId, parseProfile, Source } from '@superfaceai/parser'; +import { parseDocumentId, parseProfile,Source } from '@superfaceai/parser'; import type { ServiceClient } from '@superfaceai/service-client'; import type { UserError } from '../common/error'; @@ -17,7 +17,8 @@ export type ProfilePreparationResponse = { }; function assertProfileResponse( - input: unknown + input: unknown, + { userError }: { userError: UserError } ): asserts input is ProfilePreparationResponse { if ( typeof input === 'object' && @@ -28,24 +29,26 @@ function assertProfileResponse( const tmp = input as { id: string; profile: { source?: string } }; if (typeof tmp.profile.source !== 'string') { - throw Error( + throw userError( `Unexpected response received - missing profile source: ${JSON.stringify( tmp, null, 2 - )}` + )}`, + 1 ); } try { parseProfile(new Source(tmp.profile.source)); } catch (e) { - throw Error( + throw userError( `Unexpected response received - unable to parse profile source: ${JSON.stringify( e, null, 2 - )}` + )}`, + 1 ); } @@ -84,6 +87,7 @@ export async function newProfile( const profileResponse = await finishProfilePreparation(resultUrl, { client, + userError, }); // Supports both . and / in profile id @@ -146,7 +150,7 @@ async function startProfilePreparation( async function finishProfilePreparation( resultUrl: string, - { client }: { client: ServiceClient } + { client, userError }: { client: ServiceClient; userError: UserError } ): Promise { const resultResponse = await client.fetch(resultUrl, { method: 'GET', @@ -158,12 +162,15 @@ async function finishProfilePreparation( }); if (resultResponse.status !== 200) { - throw Error(`Unexpected status code ${resultResponse.status} received`); + throw userError( + `Unexpected status code ${resultResponse.status} received`, + 1 + ); } const body = (await resultResponse.json()) as unknown; - assertProfileResponse(body); + assertProfileResponse(body, { userError }); return body; } diff --git a/src/logic/prepare.ts b/src/logic/prepare.ts index 7d135b97..2ad12edf 100644 --- a/src/logic/prepare.ts +++ b/src/logic/prepare.ts @@ -14,7 +14,8 @@ export type ProviderPreparationResponse = { }; function assertProviderResponse( - input: unknown + input: unknown, + { userError }: { userError: UserError } ): asserts input is ProviderPreparationResponse { if ( typeof input === 'object' && @@ -39,7 +40,7 @@ function assertProviderResponse( } } - throw Error(`Unexpected response received`); + throw userError(`Unexpected response received`, 1); } export async function prepareProviderJson( @@ -141,7 +142,7 @@ async function finishProviderPreparation( const body = (await resultResponse.json()) as unknown; - assertProviderResponse(body); + assertProviderResponse(body, { userError }); return body; }