Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(server): fix formData in server action #751

Merged
merged 2 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading