Skip to content
Draft
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ 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
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
Expand Down
19 changes: 14 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
};
46 changes: 46 additions & 0 deletions static/tests/frontend-new/specs/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
Loading