diff --git a/README.md b/README.md index d7c61f7..47d317f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ pnpm run plugins i ep_post_data ## Usage POST to `/post` to create or update a pad. Set the `X-PAD-ID` header to choose the pad name, otherwise a random ID is generated. +Use `PATCH /post` to append content to an existing pad (or create it if it does not exist). ```bash # Create a pad with a random ID @@ -18,6 +19,9 @@ curl -X POST -d @datafile.txt http://localhost:9001/post # Create or update a specific pad curl -X POST -d @datafile.txt -H 'X-PAD-ID: mypad' http://localhost:9001/post + +# Append to a specific pad (or create it if missing) +curl -X PATCH -d @datafile.txt -H 'X-PAD-ID: mypad' http://localhost:9001/post ``` ## Limits diff --git a/index.js b/index.js index 169c138..3709cdb 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomStrin const MAX_BODY_SIZE = 1024 * 1024; // 1 MB exports.registerRoute = (hookName, args, callback) => { - args.app.post('/post', (req, res) => { + const upsertPadFromRequest = (req, res, shouldAppend) => { let padId = req.headers['x-pad-id']; if (padId === undefined) { padId = randomString(8); @@ -49,15 +49,24 @@ exports.registerRoute = (hookName, args, callback) => { // Pad already exists so updating an existing pad. if (padExists) { try { - console.debug('ep_post_data: Setting text!', padId, content); - await API.setText(padId, content); - res.send('Success updating pad'); + if (shouldAppend) { + console.debug('ep_post_data: Appending text!', padId, content); + await API.appendText(padId, content); + res.send('Success appending to pad'); + } else { + console.debug('ep_post_data: Setting text!', padId, content); + await API.setText(padId, content); + res.send('Success updating pad'); + } } catch (e) { console.error('ep_post_data: Error updating pad', padId, e); res.send('Error updating pad'); } } }); - }); + }; + + args.app.post('/post', (req, res) => upsertPadFromRequest(req, res, false)); + args.app.patch('/post', (req, res) => upsertPadFromRequest(req, res, true)); callback(); }; diff --git a/static/tests/frontend-new/specs/smoke.spec.ts b/static/tests/frontend-new/specs/smoke.spec.ts index 58d0d30..54db667 100644 --- a/static/tests/frontend-new/specs/smoke.spec.ts +++ b/static/tests/frontend-new/specs/smoke.spec.ts @@ -5,9 +5,55 @@ test.beforeEach(async ({page}) => { await goToNewPad(page); }); +const getPadIdFromUrl = (url: string) => { + const match = /\/p\/([^/?#]+)/.exec(url); + if (!match) throw new Error(`Failed to parse pad id from URL: ${url}`); + return match[1]; +}; + +const absoluteUrl = (pageUrl: string, path: string) => new URL(path, pageUrl).toString(); + test.describe('ep_post_data', () => { test('pad loads with plugin installed', async ({page}) => { const padBody = await getPadBody(page); await expect(padBody).toBeVisible(); }); + + test('PATCH /post appends text to an existing pad', async ({page}) => { + const pageUrl = page.url(); + const padId = getPadIdFromUrl(pageUrl); + const postUrl = absoluteUrl(pageUrl, '/post'); + + const postResponse = await page.request.post(postUrl, { + headers: {'X-PAD-ID': padId}, + data: 'first', + }); + expect(postResponse.ok()).toBeTruthy(); + + const patchResponse = await page.request.fetch(postUrl, { + method: 'PATCH', + headers: {'X-PAD-ID': padId}, + data: ' second', + }); + expect(patchResponse.ok()).toBeTruthy(); + + const txtResponse = await page.request.get(absoluteUrl(pageUrl, `/p/${padId}/export/txt`)); + expect(txtResponse.ok()).toBeTruthy(); + expect((await txtResponse.text()).trimEnd()).toBe('first second'); + }); + + test('PATCH /post creates pad when it does not exist', async ({page}) => { + const pageUrl = page.url(); + const padId = `ep-post-data-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; + const patchResponse = await page.request.fetch(absoluteUrl(pageUrl, '/post'), { + method: 'PATCH', + headers: {'X-PAD-ID': padId}, + data: 'created via patch', + }); + expect(patchResponse.ok()).toBeTruthy(); + + const txtResponse = await page.request.get(absoluteUrl(pageUrl, `/p/${padId}/export/txt`)); + expect(txtResponse.ok()).toBeTruthy(); + expect((await txtResponse.text()).trimEnd()).toBe('created via patch'); + }); });