Skip to content

Commit

Permalink
Wire package download button up to UI. There are still issues with PD…
Browse files Browse the repository at this point in the history
…F data serialization that need to be resolved.
  • Loading branch information
danielnaab committed Oct 29, 2024
1 parent dd7596c commit 84927c0
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const sidebarPatterns: DropdownPattern[] = [
['paragraph', defaultFormConfig.patterns['paragraph']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['package-download', defaultFormConfig.patterns['package-download']]
['package-download', defaultFormConfig.patterns['package-download']],
] as const;
export const fieldsetPatterns: DropdownPattern[] = [
['form-summary', defaultFormConfig.patterns['form-summary']],
Expand All @@ -103,7 +103,7 @@ export const fieldsetPatterns: DropdownPattern[] = [
['paragraph', defaultFormConfig.patterns['paragraph']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['package-download', defaultFormConfig.patterns['package-download']]
['package-download', defaultFormConfig.patterns['package-download']],
] as const;

export const SidebarAddPatternMenuItem = ({
Expand Down
3 changes: 1 addition & 2 deletions packages/forms/src/documents/pdf/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const createFormOutputFieldData = (
return;
}
const outputFieldId = output.formFields[patternId];
console.log('***', patternId, docField, outputFieldId);
if (outputFieldId === '') {
console.error(`empty outputFieldId for field: ${patternId}: ${docField}`);
return;
Expand Down Expand Up @@ -66,7 +65,7 @@ export const fillPDF = async (
.sort((a, b) => a.name.localeCompare(b.name));

// Console log the resulting array
//console.log('uniqueNamesArray:', uniqueNamesArray);
console.log('uniqueNamesArray:', uniqueNamesArray);

// fields.map(field => {
// console.log('field name is:', field.getName());
Expand Down
34 changes: 12 additions & 22 deletions packages/forms/src/pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,12 @@ export const validatePatternAndChildren = (
form: Blueprint,
patternConfig: PatternConfig,
pattern: Pattern,
values: Record<string, string>
) => {
const result: {
values: Record<string, string>,
result: {
values: Record<PatternId, PatternValue>;
errors: Record<PatternId, FormError>;
} = {
values: {},
errors: {},
};

} = { values: {}, errors: {} }
) => {
if (patternConfig.parseUserInput) {
const parseResult = patternConfig.parseUserInput(
pattern,
Expand All @@ -151,23 +147,17 @@ export const validatePatternAndChildren = (
result.errors[pattern.id] = parseResult.error;
}
}

for (const child of patternConfig.getChildren(pattern, form.patterns)) {
const childPatternConfig = getPatternConfig(config, child.type);
if (childPatternConfig.parseUserInput) {
const parseResult = childPatternConfig.parseUserInput(
child,
values[child.id]
);
if (parseResult.success) {
result.values[child.id] = parseResult.data;
} else {
result.values[child.id] = values[child.id];
result.errors[child.id] = parseResult.error;
}
}
validatePatternAndChildren(
config,
form,
childPatternConfig,
child,
values,
result
);
}

return result;
};

Expand Down
11 changes: 5 additions & 6 deletions packages/forms/src/patterns/package-download/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as z from 'zod';

import { type Pattern, type PatternConfig } from '../../pattern.js';
import { type PackageDownloadProps } from '../../components.js';
import { type ActionName, getActionString } from '../../submission.js';
import { getActionString } from '../../submission.js';
import { safeZodParseFormErrors } from '../../util/zod.js';

const configSchema = z.object({
Expand All @@ -21,18 +21,17 @@ export const packageDownloadConfig: PatternConfig<PackageDownloadPattern> = {
return [];
},
createPrompt(_, session, pattern, options) {
const actionName: ActionName = getActionString({
handlerId: 'page-set',
patternId: pattern.id,
});
return {
props: {
_patternId: pattern.id,
type: 'package-download' as const,
actions: [
{
type: 'submit',
submitAction: actionName,
submitAction: getActionString({
handlerId: 'package-download',
patternId: pattern.id,
}),
text: 'Download PDF',
},
],
Expand Down
5 changes: 2 additions & 3 deletions packages/forms/src/patterns/package-download/submit.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { describe, expect, it } from 'vitest';

import { failure, success } from '@atj/common';
import { failure } from '@atj/common';

import { Blueprint, getDocumentFieldData, type FormSession } from '../..';
import { defaultFormConfig } from '../../..';
import { type Blueprint, type FormSession, defaultFormConfig } from '../..';

import { downloadPackageHandler } from './submit';
import { PackageDownload } from './builder';
Expand Down
37 changes: 19 additions & 18 deletions packages/forms/src/repository/get-form.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { expect, it } from 'vitest';

import { type DbTestContext, describeDatabase } from '@atj/database/testing';

import { type Blueprint } from '../index.js';
import { getForm } from './get-form.js';

describeDatabase('getForm', () => {
Expand All @@ -10,31 +12,16 @@ describeDatabase('getForm', () => {
.insertInto('forms')
.values({
id: '45c66187-64e2-4d75-a45a-e80f1d035bc5',
data: '{"summary":{"title":"Test form","description":"Test description"},"root":"root","patterns":{"root":{"type":"sequence","id":"root","data":{"patterns":[]}}},"outputs":[]}',
data: '{"summary":{"title":"Title","description":"Description"},"root":"root","patterns":{"root":{"type":"sequence","id":"root","data":{"patterns":[]}}},"outputs":[{"data":"AQID","path":"test.pdf","fields":{},"formFields":{}}]}',
})
.execute();

const result = await getForm(
db.ctx,
'45c66187-64e2-4d75-a45a-e80f1d035bc5'
);
expect(result).toEqual({
outputs: [],
patterns: {
root: {
data: {
patterns: [],
},
id: 'root',
type: 'sequence',
},
},
root: 'root',
summary: {
description: 'Test description',
title: 'Test form',
},
});
console.log(result);
expect(result).toEqual(TEST_FORM);
});

it<DbTestContext>('return null with non-existent form', async ({ db }) => {
Expand All @@ -45,3 +32,17 @@ describeDatabase('getForm', () => {
expect(result).toBeNull();
});
});

const TEST_FORM: Blueprint = {
summary: { title: 'Title', description: 'Description' },
root: 'root',
patterns: { root: { type: 'sequence', id: 'root', data: { patterns: [] } } },
outputs: [
{
data: new Uint8Array([1, 2, 3]),
path: 'test.pdf',
fields: {},
formFields: {},
},
],
};
2 changes: 1 addition & 1 deletion packages/forms/src/repository/get-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { type Blueprint } from '../index.js';
export type GetForm = (
ctx: DatabaseContext,
formId: string
) => Promise</*Blueprint*/ any | null>;
) => Promise<Blueprint | null>;

export const getForm: GetForm = async (ctx, formId) => {
const db = await ctx.getKysely();
Expand Down
14 changes: 11 additions & 3 deletions packages/forms/src/repository/save-form.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { expect, it } from 'vitest';

import { type DbTestContext, describeDatabase } from '@atj/database/testing';

import { type Blueprint } from '../index.js';
import { saveForm } from './save-form.js';
import { addForm } from './add-form.js';

const TEST_FORM = {
const TEST_FORM: Blueprint = {
summary: {
title: 'Test form',
description: 'Test description',
Expand All @@ -20,7 +21,14 @@ const TEST_FORM = {
},
},
},
outputs: [],
outputs: [
{
data: new Uint8Array([1, 2, 3]),
path: 'test.pdf',
fields: {},
formFields: {},
},
],
};

describeDatabase('saveForm', () => {
Expand All @@ -47,7 +55,7 @@ describeDatabase('saveForm', () => {

expect(result[0].id).toEqual(addResult.data.id);
expect(result[0].data).toEqual(
'{"summary":{"title":"Updated title","description":"Updated description"},"root":"root","patterns":{"root":{"type":"sequence","id":"root","data":{"patterns":[]}}},"outputs":[]}'
'{"summary":{"title":"Updated title","description":"Updated description"},"root":"root","patterns":{"root":{"type":"sequence","id":"root","data":{"patterns":[]}}},"outputs":[{"data":"AQID","path":"test.pdf","fields":{},"formFields":{}}]}'
);
});
});
4 changes: 3 additions & 1 deletion packages/forms/src/repository/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const stringifyForm = (form: /*Blueprint*/ any) => {
import { type Blueprint } from '..';

export const stringifyForm = (form: Blueprint) => {
return JSON.stringify({
...form,
outputs: form.outputs.map((output: any) => ({
Expand Down
18 changes: 4 additions & 14 deletions packages/forms/src/services/submit-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { FormServiceContext } from '../context/index.js';
import { submitPage } from '../patterns/page-set/submit';
import { downloadPackageHandler } from '../patterns/package-download/submit';
import { type FormRoute } from '../route-data.js';
import { getActionString, SubmissionRegistry } from '../submission';
import { SubmissionRegistry } from '../submission';

export type SubmitForm = (
ctx: FormServiceContext,
Expand All @@ -23,7 +23,7 @@ export type SubmitForm = (
Result<{
sessionId: FormSessionId;
session: FormSession;
documents?: {
attachments?: {
fileName: string;
data: Uint8Array;
}[];
Expand Down Expand Up @@ -70,21 +70,12 @@ export const submitForm: SubmitForm = async (
}
: sessionResult.data;

const actionString = formData.action;
const actionString = formData['action'];
if (typeof actionString !== 'string') {
return failure(`Invalid action: ${actionString}`);
}

// Get the root pattern which should be a page-set
const rootPatternId = form.root;
const submitHandlerResult = registry.getHandlerForAction(
form,
getActionString({
handlerId: 'page-set',
patternId: rootPatternId,
})
);

const submitHandlerResult = registry.getHandlerForAction(form, actionString);
if (!submitHandlerResult.success) {
return failure(submitHandlerResult.error);
}
Expand Down Expand Up @@ -140,7 +131,6 @@ const getFormSessionOrCreate = async (
route?: FormRoute,
sessionId?: FormSessionId
) => {
console.log('got sessionId', sessionId);
if (sessionId === undefined) {
return success(createFormSession(form, route));
}
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/lib/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export class FormServiceClient implements FormService {
'Content-Type': 'application/json',
},
});
return await response.json();
const result = await response.json();
console.log('addForm result', result);
return result;
}

async deleteForm(formId: string) {
Expand Down
45 changes: 45 additions & 0 deletions packages/server/src/lib/attachments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export const createMultipartResponse = (
pdfs: { fileName: string; data: Uint8Array }[]
): Response => {
const boundary = createBoundary();

// Array to store each part of the multipart message
const parts: Uint8Array[] = pdfs.flatMap(pdf => {
const headers = [
`--${boundary}`,
`Content-Type: application/pdf`,
`Content-Disposition: attachment; filename="${pdf.fileName}"`,
'',
'', // empty line between headers and content
].join('\r\n');

return [stringToUint8Array(headers), pdf.data, stringToUint8Array('\r\n')];
});

// Final boundary to mark the end of the message
parts.push(stringToUint8Array(`--${boundary}--`));

// Concatenate all Uint8Array parts into a single Uint8Array body
const body = new Uint8Array(
parts.reduce((sum, part) => sum + part.length, 0)
);
let offset = 0;
parts.forEach(part => {
body.set(part, offset);
offset += part.length;
});

return new Response(body, {
status: 200,
headers: {
'Content-Type': `multipart/mixed; boundary=${boundary}`,
'Content-Length': body.length.toString(),
},
});
};

const createBoundary = (): string =>
`boundary_${Math.random().toString(36).slice(2)}`;

const stringToUint8Array = (str: string): Uint8Array =>
new TextEncoder().encode(str);
5 changes: 5 additions & 0 deletions packages/server/src/pages/forms/[id].astro
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ if (Astro.request.method === 'POST') {
});
}
setFormSessionCookie(submitFormResult.data.sessionId);
if (submitFormResult.data.attachments) {
return createMultipartResponse(submitFormResult.data.attachments);
}
return Astro.redirect(getNextUrl(submitFormResult.data.session.route));
}
Expand Down

0 comments on commit 84927c0

Please sign in to comment.