diff --git a/packages/app/src/cli/services/app-logs/dev/poll-app-logs.test.ts b/packages/app/src/cli/services/app-logs/dev/poll-app-logs.test.ts index f6a28217d3..fd81b72c10 100644 --- a/packages/app/src/cli/services/app-logs/dev/poll-app-logs.test.ts +++ b/packages/app/src/cli/services/app-logs/dev/poll-app-logs.test.ts @@ -1,10 +1,12 @@ import {pollAppLogs} from './poll-app-logs.js' import {writeAppLogsToFile} from './write-app-logs.js' +import {FunctionRunLog} from '../types.js' import {partnersFqdn} from '@shopify/cli-kit/node/context/fqdn' import {describe, expect, test, vi, beforeEach, afterEach} from 'vitest' import {fetch} from '@shopify/cli-kit/node/http' import * as components from '@shopify/cli-kit/node/ui/components' import * as output from '@shopify/cli-kit/node/output' +import camelcaseKeys from 'camelcase-keys' const JWT_TOKEN = 'jwtToken' const API_KEY = 'apiKey' @@ -264,15 +266,22 @@ describe('pollAppLogs', () => { }, }) + const appLogPayloadZero = new FunctionRunLog( + camelcaseKeys(JSON.parse(RESPONSE_DATA.app_logs[0]!.payload), {deep: true}), + ) expect(writeAppLogsToFile).toHaveBeenCalledWith({ appLog: RESPONSE_DATA.app_logs[0], - appLogPayload: JSON.parse(RESPONSE_DATA.app_logs[0]!.payload), + appLogPayload: appLogPayloadZero, apiKey: API_KEY, stdout, }) + + const appLogPayloadOne = new FunctionRunLog( + camelcaseKeys(JSON.parse(RESPONSE_DATA.app_logs[1]!.payload), {deep: true}), + ) expect(writeAppLogsToFile).toHaveBeenCalledWith({ appLog: RESPONSE_DATA.app_logs[1], - appLogPayload: JSON.parse(RESPONSE_DATA.app_logs[1]!.payload), + appLogPayload: appLogPayloadOne, apiKey: API_KEY, stdout, }) diff --git a/packages/app/src/cli/services/app-logs/dev/poll-app-logs.ts b/packages/app/src/cli/services/app-logs/dev/poll-app-logs.ts index 6feca18364..2bff1282ac 100644 --- a/packages/app/src/cli/services/app-logs/dev/poll-app-logs.ts +++ b/packages/app/src/cli/services/app-logs/dev/poll-app-logs.ts @@ -13,9 +13,10 @@ import { REQUEST_EXECUTION_IN_BACKGROUND_CACHE_ABOUT_TO_EXPIRE_REASON, handleFetchAppLogsError, } from '../utils.js' -import {AppLogData, ErrorResponse} from '../types.js' +import {AppLogData, ErrorResponse, FunctionRunLog} from '../types.js' import {outputContent, outputDebug, outputToken, outputWarn} from '@shopify/cli-kit/node/output' import {useConcurrentOutputContext} from '@shopify/cli-kit/node/ui/components' +import camelcaseKeys from 'camelcase-keys' import {Writable} from 'stream' export const pollAppLogs = async ({ @@ -74,12 +75,12 @@ export const pollAppLogs = async ({ const {app_logs: appLogs} = data for (const log of appLogs) { - const payload = JSON.parse(log.payload) - + let payload = JSON.parse(log.payload) // eslint-disable-next-line no-await-in-loop await useConcurrentOutputContext({outputPrefix: log.source, stripAnsi: false}, async () => { if (log.log_type === LOG_TYPE_FUNCTION_RUN) { handleFunctionRunLog(log, payload, stdout) + payload = new FunctionRunLog(camelcaseKeys(payload, {deep: true})) } else if (log.log_type.startsWith(LOG_TYPE_FUNCTION_NETWORK_ACCESS)) { handleFunctionNetworkAccessLog(log, payload, stdout) } else { diff --git a/packages/app/src/cli/services/app-logs/dev/write-app-logs.test.ts b/packages/app/src/cli/services/app-logs/dev/write-app-logs.test.ts index 4a1f956579..3ec4366490 100644 --- a/packages/app/src/cli/services/app-logs/dev/write-app-logs.test.ts +++ b/packages/app/src/cli/services/app-logs/dev/write-app-logs.test.ts @@ -1,6 +1,5 @@ import {writeAppLogsToFile} from './write-app-logs.js' -import {AppLogData} from '../types.js' -import {LOG_TYPE_FUNCTION_RUN} from '../utils.js' +import {AppLogData, AppLogPayload, FunctionRunLog} from '../types.js' import {joinPath} from '@shopify/cli-kit/node/path' import {writeLog} from '@shopify/cli-kit/node/logs' import {describe, expect, test, vi, beforeEach} from 'vitest' @@ -19,6 +18,20 @@ const APP_LOG: AppLogData = { source_namespace: 'extensions', log_timestamp: '2024-05-22T15:06:41.827379Z', } + +const NEW_APP_LOG: AppLogData = { + shop_id: 1, + api_client_id: 2, + payload: JSON.stringify({someJson: 'someJSOn'}), + log_type: 'new_app_log_type', + cursor: '2024-05-22T15:06:43.841156Z', + status: 'success', + source: 'my-function', + source_namespace: 'extensions', + log_timestamp: '2024-05-22T15:06:41.827379Z', +} + +const FUNCTION_RUN_PAYLOAD = new FunctionRunLog(camelcaseKeys(JSON.parse(APP_LOG.payload))) const API_KEY = 'apiKey' describe('writeAppLogsToFile', () => { @@ -28,7 +41,7 @@ describe('writeAppLogsToFile', () => { stdout = {write: vi.fn()} }) - test('calls writeLog with the right data', async () => { + test('calls writeLog with the FunctionRunLog payload type', async () => { // Given // determine the fileName and path const fileName = `20240522_150641_827Z_${APP_LOG.source_namespace}_${APP_LOG.source}` @@ -37,29 +50,54 @@ describe('writeAppLogsToFile', () => { // When const returnedPath = await writeAppLogsToFile({ appLog: APP_LOG, - appLogPayload: JSON.parse(APP_LOG.payload), + appLogPayload: FUNCTION_RUN_PAYLOAD, apiKey: API_KEY, stdout, }) // Then expect(returnedPath.fullOutputPath.startsWith(path)).toBe(true) - expect(writeLog).toHaveBeenCalledWith(expect.stringContaining(path), expectedLogDataFromAppEvent(APP_LOG)) + expect(writeLog).toHaveBeenCalledWith( + expect.stringContaining(path), + expectedLogDataFromAppEvent(APP_LOG, FUNCTION_RUN_PAYLOAD), + ) }) -}) -function expectedLogDataFromAppEvent(event: AppLogData): string { - const payload = JSON.parse(event.payload) + test('calls writeLog with strings when no matching payload type', async () => { + // Given + // determine the fileName and path + const fileName = `20240522_150641_827Z_${APP_LOG.source_namespace}_${APP_LOG.source}` + const path = joinPath(API_KEY, fileName) + + // When + const returnedPath = await writeAppLogsToFile({ + appLog: NEW_APP_LOG, + appLogPayload: JSON.parse(NEW_APP_LOG.payload), + apiKey: API_KEY, + stdout, + }) - if (event.log_type === LOG_TYPE_FUNCTION_RUN) { - payload.logs = payload.logs.split('\n').filter(Boolean) - } + // Then + expect(returnedPath.fullOutputPath.startsWith(path)).toBe(true) + expect(writeLog).toHaveBeenCalledWith( + expect.stringContaining(path), + expectedLogDataFromAppEvent(NEW_APP_LOG, JSON.parse(NEW_APP_LOG.payload)), + ) + }) +}) + +function expectedLogDataFromAppEvent(event: AppLogData, payload: AppLogPayload | string): string { const data = camelcaseKeys( { ...event, - payload, + payload: payload as any, }, {deep: true}, ) + + if (payload instanceof FunctionRunLog) { + data.payload.logs = payload.logs.split('\n').filter(Boolean) + } + return JSON.stringify(data, null, 2) } diff --git a/packages/app/src/cli/services/app-logs/dev/write-app-logs.ts b/packages/app/src/cli/services/app-logs/dev/write-app-logs.ts index 26e17cec7c..636e0112c8 100644 --- a/packages/app/src/cli/services/app-logs/dev/write-app-logs.ts +++ b/packages/app/src/cli/services/app-logs/dev/write-app-logs.ts @@ -1,5 +1,4 @@ -import {AppLogData} from '../types.js' -import {LOG_TYPE_FUNCTION_RUN} from '../utils.js' +import {AppLogData, AppLogPayload, FunctionRunLog} from '../types.js' import {joinPath} from '@shopify/cli-kit/node/path' import {writeLog, getLogsDir} from '@shopify/cli-kit/node/logs' import {randomUUID} from '@shopify/cli-kit/node/crypto' @@ -19,7 +18,7 @@ export const writeAppLogsToFile = async ({ }: { appLog: AppLogData // eslint-disable-next-line @typescript-eslint/no-explicit-any - appLogPayload: any + appLogPayload: AppLogPayload | any apiKey: string stdout: Writable }): Promise => { @@ -31,10 +30,6 @@ export const writeAppLogsToFile = async ({ const fullOutputPath = joinPath(getLogsDir(), path) try { - if (appLog.log_type === LOG_TYPE_FUNCTION_RUN) { - appLogPayload.logs = appLogPayload.logs.split('\n').filter(Boolean) - } - const toSaveData = camelcaseKeys( { ...appLog, @@ -43,6 +38,10 @@ export const writeAppLogsToFile = async ({ {deep: true}, ) + if (appLogPayload instanceof FunctionRunLog) { + toSaveData.payload.logs = appLogPayload.logs.split('\n').filter(Boolean) + } + const logData = JSON.stringify(toSaveData, null, 2) await writeLog(path, logData)