Skip to content

Commit

Permalink
fix(server): fix formData in server action (#751)
Browse files Browse the repository at this point in the history
* fix(server): fix formData in server action

* test: fix tests
  • Loading branch information
aralroca authored Feb 7, 2025
1 parent 75b4f2b commit 976a991
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 30 deletions.
32 changes: 32 additions & 0 deletions packages/brisa/src/cli/serve/serve-options.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { RequestContext } from '@/types';
import { Initiator } from '@/public-constants';
import { AVOID_DECLARATIVE_SHADOW_DOM_SYMBOL } from '@/utils/ssr-web-component';
import { getServeOptions } from './serve-options';
import type { RequestContent } from '@/utils/transfer-store-service';
import {
ENCRYPT_NONTEXT_PREFIX,
encrypt,
Expand Down Expand Up @@ -1707,6 +1708,37 @@ describe.each(BASE_PATHS)('CLI: serve %s', (basePath) => {
);
});

it('should the response action receive the formData', async () => {
const mockResponseAction = mock(
(req: RequestContext, content: RequestContent) => {},
);
const formData = new FormData();
formData.append('foo', 'bar');
formData.append('x-s', '[["some", "value"]]');

mock.module('@/utils/response-action', () => ({
default: (req: RequestContext, content: RequestContent) =>
mockResponseAction(req, content),
}));

await testRequest(
new Request(`http://localhost:1234${basePath}/es/somepage`, {
method: 'POST',
body: formData,
headers: {
'x-action': 'a1_1',
},
}),
);

const [req, reqContent] = mockResponseAction.mock.calls[0];

expect(req.store.get('some')).toBe('value');
expect(Array.from(reqContent.formData!.entries())).toEqual([
['foo', 'bar'],
]);
});

it('should have req.initiator with "SPA_NAVIGATION" when the Page is POST method without x-action header', async () => {
globalThis.mockConstants = {
...globalThis.mockConstants,
Expand Down
11 changes: 7 additions & 4 deletions packages/brisa/src/cli/serve/serve-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import getReadableStreamFromPath from '@/utils/get-readable-stream-from-path';
import getContentTypeFromPath from '@/utils/get-content-type-from-path';
import getInitiator from '@/utils/get-initiator';
import { handleSPARedirects } from '@/utils/hard-to-soft-redirect';
import transferStoreService from '@/utils/transfer-store-service';
import transferStoreService, {
type RequestContent,
} from '@/utils/transfer-store-service';

export async function getServeOptions() {
setUpEnvVars();
Expand Down Expand Up @@ -249,14 +251,15 @@ export async function getServeOptions() {
const isApi = initiator === Initiator.API_REQUEST;
const api = isApi ? rootRouter.match(req) : null;
const isPOST = req.method === 'POST';
let reqContent: RequestContent | undefined;

req.initiator = initiator;
req.route = (isApi ? api?.route : route) as MatchedBrisaRoute;

// Transfer once the client store to server (middleware, actions, navigate...)
if (isPOST && !isApi) {
const { transferClientStoreToServer } = await transferStoreService(req);
transferClientStoreToServer();
reqContent = await transferStoreService(req);
reqContent.transferClientStoreToServer();
}

// Middleware
Expand All @@ -283,7 +286,7 @@ export async function getServeOptions() {

// Actions
if (initiator === Initiator.SERVER_ACTION) {
return responseAction(req);
return responseAction(req, reqContent!);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const i18nCode = 3653;
const brisaSize = 5721; // TODO: Reduce this size :/
const webComponents = 1118;
const unsuspenseSize = 213;
const rpcSize = 2509; // TODO: Reduce this size
const lazyRPCSize = 4371; // TODO: Reduce this size
const rpcSize = 2500; // TODO: Reduce this size
const lazyRPCSize = 4308; // TODO: Reduce this size
// lazyRPC is loaded after user interaction (action, link),
// so it's not included in the initial size
const initialSize = unsuspenseSize + rpcSize;
Expand Down
2 changes: 1 addition & 1 deletion packages/brisa/src/utils/compile-files/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ describe('utils', () => {
${info}Ω /i18n | 162 B |
${info}Ψ /websocket | 207 B |
${info}Θ /web-components/_integrations | 67 B |
${info}λ /pages/index | 827 B | ${greenLog('4 kB')}
${info}λ /pages/index | 827 B | ${greenLog('3 kB')}
${info}λ /pages/page-with-web-component | 761 B | ${greenLog('5 kB')}
${info}λ /pages/somepage | 1 kB | ${greenLog('0 B')}
${info}λ /pages/somepage-with-context | 1 kB | ${greenLog('0 B')}
Expand Down
59 changes: 39 additions & 20 deletions packages/brisa/src/utils/response-action/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import responseAction from '.';
import { getConstants } from '@/constants';
import { boldLog } from '@/utils/log/log-color';
import { normalizeHTML } from '@/helpers';
import transferStoreService from '@/utils/transfer-store-service';

const FIXTURES = path.join(import.meta.dir, '..', '..', '__fixtures__');
const PAGE = 'http://locahost/es/somepage';
Expand Down Expand Up @@ -48,8 +49,9 @@ describe('utils', () => {
}),
}),
});
const reqContent = await transferStoreService(req);

const res = await responseAction(req);
const res = await responseAction(req, reqContent);
const resBody = await res.json();

expect(resBody).toEqual([]);
Expand All @@ -72,8 +74,9 @@ describe('utils', () => {
});

req.formData = async () => formData;
const reqContent = await transferStoreService(req);

const res = await responseAction(req);
const res = await responseAction(req, reqContent);

expect(req.store.get('__params:a1_1')).toEqual([
{
Expand Down Expand Up @@ -128,8 +131,8 @@ describe('utils', () => {
});

req.formData = async () => formData;

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(res.headers.get('x-reset')).toBe('1');
});
Expand All @@ -150,7 +153,8 @@ describe('utils', () => {

req.formData = async () => formData;

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(req.store.get('__params:a1_1')).toEqual([
{
Expand Down Expand Up @@ -221,7 +225,8 @@ describe('utils', () => {
}),
});

await responseAction(req);
const reqContent = await transferStoreService(req);
await responseAction(req, reqContent);

expect(req.store.get('__params:a1_1')).toEqual([{ foo: 'bar' }, 'bar']);
});
Expand All @@ -245,7 +250,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const resBody = await res.json();

expect(resBody).toEqual([]);
Expand Down Expand Up @@ -275,7 +281,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const resBody = await res.json();

expect(resBody).toEqual([]);
Expand Down Expand Up @@ -308,7 +315,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const resBody = await res.json();

expect(resBody).toEqual([]);
Expand All @@ -334,7 +342,8 @@ describe('utils', () => {
}),
});

await responseAction(req);
const reqContent = await transferStoreService(req);
await responseAction(req, reqContent);

// @ts-ignore
expect(req._p).toBeTypeOf('function');
Expand All @@ -358,7 +367,8 @@ describe('utils', () => {
}),
});

await responseAction(req);
const reqContent = await transferStoreService(req);
await responseAction(req, reqContent);

const logs = logMock.mock.calls.toString();

Expand Down Expand Up @@ -392,7 +402,8 @@ describe('utils', () => {
}),
});

await responseAction(req);
const reqContent = await transferStoreService(req);
await responseAction(req, reqContent);

const logs = logMock.mock.calls.toString();

Expand Down Expand Up @@ -422,7 +433,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(await res.text()).toBe('a3_5');
});
Expand All @@ -443,7 +455,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(await res.text()).toBe('a3_5');
});
Expand All @@ -463,7 +476,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const resBody = await res.text();

expect(resBody).toBe('a3_7 error');
Expand All @@ -485,7 +499,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const resBody = await res.text();

expect(resBody).toBe('a3_7 error');
Expand All @@ -508,7 +523,8 @@ describe('utils', () => {
} as any,
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(res.headers.get('x-test')).toBe('test');
});
Expand All @@ -530,7 +546,8 @@ describe('utils', () => {
} as any,
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);

expect(res.headers.get('x-test')).toBe('success');
});
Expand All @@ -547,7 +564,8 @@ describe('utils', () => {
}),
});

const res = await responseAction(req);
const reqContent = await transferStoreService(req);
const res = await responseAction(req, reqContent);
const logMessage = logMock.mock.calls.toString();

expect(logMock).toHaveBeenCalled();
Expand Down Expand Up @@ -577,7 +595,8 @@ describe('utils', () => {
}),
});

await responseAction(req);
const reqContent = await transferStoreService(req);
await responseAction(req, reqContent);

expect(req.store.get(Symbol.for('DEPENDENCIES'))).toEqual([
[['onClick', 'a1_2']],
Expand Down
9 changes: 6 additions & 3 deletions packages/brisa/src/utils/response-action/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RequestContext } from '@/types';
import { deserialize } from '@/utils/serialization';
import transferStoreService, {
import {
type RequestContent,
resolveStore,
} from '@/utils/transfer-store-service';
import { logError } from '@/utils/log/log-build';
Expand All @@ -10,10 +11,12 @@ import { getConstants } from '@/constants';

const DEPENDENCIES = Symbol.for('DEPENDENCIES');

export default async function responseAction(req: RequestContext) {
export default async function responseAction(
req: RequestContext,
{ formData, body }: RequestContent,
) {
const { BUILD_DIR } = getConstants();
const actionModule = await importFileIfExists('actions', BUILD_DIR);
const { formData, body } = await transferStoreService(req);
const url = new URL(req.url);
const action =
req.headers.get('x-action') ?? url.searchParams.get('_aid') ?? '';
Expand Down
2 changes: 2 additions & 0 deletions packages/brisa/src/utils/transfer-store-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '@/utils/crypto';
import { logError } from '@/utils/log/log-build';

export type RequestContent = Awaited<ReturnType<typeof transferStoreService>>;

export default async function transferStoreService(req: RequestContext) {
const contentType = req.headers.get('content-type');
const reqClone = req.clone();
Expand Down

0 comments on commit 976a991

Please sign in to comment.