mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
API: Added more calls
This commit is contained in:
parent
a9e789f845
commit
eb4aa2c026
@ -52,9 +52,6 @@ class Command extends BaseCommand {
|
||||
|
||||
const lines = [];
|
||||
|
||||
// Get list of note tags
|
||||
// Get list of folder notes
|
||||
|
||||
lines.push('# Joplin API');
|
||||
lines.push('');
|
||||
|
||||
@ -98,7 +95,7 @@ class Command extends BaseCommand {
|
||||
lines.push('The four verbs supported by the API are the following ones:');
|
||||
lines.push('');
|
||||
lines.push('* **GET**: To retrieve items (notes, notebooks, etc.).');
|
||||
lines.push('* **POST**: To create new items.');
|
||||
lines.push('* **POST**: To create new items. In general most item properties are optional. If you omit any, a default value will be used.');
|
||||
lines.push('* **PUT**: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won\'t be cleared nor changed).');
|
||||
lines.push('* **DELETE**: To delete items.');
|
||||
lines.push('');
|
||||
@ -110,13 +107,42 @@ class Command extends BaseCommand {
|
||||
lines.push('* Booleans are integer values 0 or 1.');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Testing if the service is available');
|
||||
lines.push('');
|
||||
lines.push('Call **GET /ping** to check if the service is available. It should return "JoplinClipperServer" if it works.');
|
||||
lines.push('');
|
||||
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
const model = models[i];
|
||||
const ModelClass = BaseItem.getClassByItemType(model.type);
|
||||
const tableName = ModelClass.tableName();
|
||||
const tableFields = reg.db().tableFields(tableName, { includeDescription: true });
|
||||
let tableFields = reg.db().tableFields(tableName, { includeDescription: true });
|
||||
const singular = tableName.substr(0, tableName.length - 1);
|
||||
|
||||
if (model.type === BaseModel.TYPE_NOTE) {
|
||||
tableFields = tableFields.slice();
|
||||
tableFields.push({
|
||||
name: 'body_html',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'Note body, in HTML format',
|
||||
});
|
||||
tableFields.push({
|
||||
name: 'base_url',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the \'?\'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`.',
|
||||
});
|
||||
tableFields.push({
|
||||
name: 'image_data_url',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.',
|
||||
});
|
||||
tableFields.push({
|
||||
name: 'crop_rect',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`',
|
||||
});
|
||||
}
|
||||
|
||||
lines.push('# ' + toTitleCase(tableName));
|
||||
lines.push('');
|
||||
|
||||
@ -135,6 +161,11 @@ class Command extends BaseCommand {
|
||||
lines.push('Gets all ' + tableName);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_FOLDER) {
|
||||
lines.push('The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the `children` key.');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('## GET /' + tableName + '/:id');
|
||||
lines.push('');
|
||||
lines.push('Gets ' + singular + ' with ID :id');
|
||||
@ -143,7 +174,21 @@ class Command extends BaseCommand {
|
||||
if (model.type === BaseModel.TYPE_TAG) {
|
||||
lines.push('## GET /tags/:id/notes');
|
||||
lines.push('');
|
||||
lines.push('Get all the notes with this tag.');
|
||||
lines.push('Gets all the notes with this tag.');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (model.type === BaseModel.TYPE_NOTE) {
|
||||
lines.push('## GET /notes/:id/tags');
|
||||
lines.push('');
|
||||
lines.push('Gets all the tags attached to this note.');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (model.type === BaseModel.TYPE_FOLDER) {
|
||||
lines.push('## GET /folders/:id/notes');
|
||||
lines.push('');
|
||||
lines.push('Gets all the notes inside this folder.');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
@ -168,6 +213,25 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (model.type === BaseModel.TYPE_NOTE) {
|
||||
lines.push('You can either specify the note body as Markdown by setting the `body` parameter, or in HTML by setting the `body_html`.');
|
||||
lines.push('');
|
||||
lines.push('Examples:');
|
||||
lines.push('');
|
||||
lines.push('* Create a note from some Markdown text');
|
||||
lines.push('');
|
||||
lines.push(' curl --data \'{ "title": "My note", "body": "Some note in **Markdown**"}\' http://127.0.0.1:41184/notes');
|
||||
lines.push('');
|
||||
lines.push('* Create a note from some HTML');
|
||||
lines.push('');
|
||||
lines.push(' curl --data \'{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}\' http://127.0.0.1:41184/notes');
|
||||
lines.push('');
|
||||
lines.push('* Create a note and attach an image to it:');
|
||||
lines.push('');
|
||||
lines.push(' curl --data \'{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}\' http://127.0.0.1:41184/notes');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('## PUT /' + tableName + '/:id');
|
||||
lines.push('');
|
||||
lines.push('Sets the properties of the ' + singular + ' with ID :id');
|
||||
@ -181,7 +245,7 @@ class Command extends BaseCommand {
|
||||
if (model.type === BaseModel.TYPE_TAG) {
|
||||
lines.push('## DELETE /tags/:id/notes/:note_id');
|
||||
lines.push('');
|
||||
lines.push('Remove the tag from the note..');
|
||||
lines.push('Remove the tag from the note.');
|
||||
lines.push('');
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,19 @@ describe('services_rest_Api', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should get the folder notes', async (done) => {
|
||||
let f1 = await Folder.save({ title: "mon carnet" });
|
||||
const response2 = await api.route('GET', 'folders/' + f1.id + '/notes');
|
||||
expect(response2.length).toBe(0);
|
||||
|
||||
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
|
||||
const n2 = await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const response = await api.route('GET', 'folders/' + f1.id + '/notes');
|
||||
expect(response.length).toBe(2);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail on invalid paths', async (done) => {
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'schtroumpf'));
|
||||
expect(hasThrown).toBe(true);
|
||||
@ -111,13 +124,6 @@ describe('services_rest_Api', function() {
|
||||
response = await api.route('GET', 'notes');
|
||||
expect(response.length).toBe(3);
|
||||
|
||||
response = await api.route('GET', 'notes', { parent_id: f1.id });
|
||||
expect(response.length).toBe(2);
|
||||
|
||||
response = await api.route('GET', 'notes', { parent_id: f2.id });
|
||||
expect(response.length).toBe(1);
|
||||
expect(response[0].id).toBe(n3.id);
|
||||
|
||||
response = await api.route('GET', 'notes/' + n1.id);
|
||||
expect(response.id).toBe(n1.id);
|
||||
|
||||
@ -243,6 +249,7 @@ describe('services_rest_Api', function() {
|
||||
|
||||
it('should list all tag notes', async (done) => {
|
||||
const tag = await Tag.save({ title: "mon étiquette" });
|
||||
const tag2 = await Tag.save({ title: "mon étiquette 2" });
|
||||
const note1 = await Note.save({ title: "ma note un" });
|
||||
const note2 = await Note.save({ title: "ma note deux" });
|
||||
await Tag.addNote(tag.id, note1.id);
|
||||
@ -250,6 +257,14 @@ describe('services_rest_Api', function() {
|
||||
|
||||
const response = await api.route('GET', 'tags/' + tag.id + '/notes');
|
||||
expect(response.length).toBe(2);
|
||||
expect('id' in response[0]).toBe(true);
|
||||
expect('title' in response[0]).toBe(true);
|
||||
|
||||
const response2 = await api.route('GET', 'notes/' + note1.id + '/tags');
|
||||
expect(response2.length).toBe(1);
|
||||
await Tag.addNote(tag2.id, note1.id);
|
||||
const response3 = await api.route('GET', 'notes/' + note1.id + '/tags');
|
||||
expect(response3.length).toBe(2);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -170,6 +170,7 @@ class JoplinDatabase extends Database {
|
||||
is_todo: _('Tells whether this note is a todo or not.'),
|
||||
todo_due: _('When the todo is due. An alarm will be triggered on that date.'),
|
||||
todo_completed: _('Tells whether todo is completed or not. This is a timestamp in milliseconds.'),
|
||||
source_url: _('The full URL where the note comes from.'),
|
||||
},
|
||||
folders: {},
|
||||
resources: {},
|
||||
|
@ -231,6 +231,15 @@ class Api {
|
||||
return await Folder.allAsTree({ fields: this.fields_(request, ['id', 'parent_id', 'title']) });
|
||||
}
|
||||
|
||||
if (request.method === 'GET' && id) {
|
||||
if (link && link === 'notes') {
|
||||
const options = this.notePreviewsOptions_(request);
|
||||
return Note.previews(id, options);
|
||||
} else if (link) {
|
||||
throw new ErrorNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
return this.defaultAction_(BaseModel.TYPE_FOLDER, request, id, link);
|
||||
}
|
||||
|
||||
@ -253,7 +262,15 @@ class Api {
|
||||
}
|
||||
|
||||
if (request.method === 'GET') {
|
||||
return await Tag.noteIds(tag.id);
|
||||
// Ideally we should get all this in one SQL query but for now that will do
|
||||
const noteIds = await Tag.noteIds(tag.id);
|
||||
const output = [];
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const n = await Note.preview(noteIds[i], this.notePreviewsOptions_(request));
|
||||
if (!n) continue;
|
||||
output.push(n);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,20 +299,29 @@ class Api {
|
||||
return this.defaultAction_(BaseModel.TYPE_RESOURCE, request, id, link);
|
||||
}
|
||||
|
||||
async action_notes(request, id = null, link = null) {
|
||||
if (request.method === 'GET') {
|
||||
this.checkToken_(request);
|
||||
|
||||
const noteId = id;
|
||||
const parentId = request.query.parent_id ? request.query.parent_id : null;
|
||||
notePreviewsOptions_(request) {
|
||||
const fields = this.fields_(request, []); // previews() already returns default fields
|
||||
const options = {};
|
||||
if (fields.length) options.fields = fields;
|
||||
return options;
|
||||
}
|
||||
|
||||
if (noteId) {
|
||||
return await Note.preview(noteId, options);
|
||||
async action_notes(request, id = null, link = null) {
|
||||
this.checkToken_(request);
|
||||
|
||||
if (request.method === 'GET') {
|
||||
|
||||
if (link && link === 'tags') {
|
||||
return Tag.tagsByNoteId(id);
|
||||
} else if (link) {
|
||||
throw new ErrorNotFound();
|
||||
}
|
||||
|
||||
const options = this.notePreviewsOptions_(request);
|
||||
if (id) {
|
||||
return await Note.preview(id, options);
|
||||
} else {
|
||||
return await Note.previews(parentId, options);
|
||||
return await Note.previews(null, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
</code></pre><p>The four verbs supported by the API are the following ones:</p>
|
||||
<ul>
|
||||
<li><strong>GET</strong>: To retrieve items (notes, notebooks, etc.).</li>
|
||||
<li><strong>POST</strong>: To create new items.</li>
|
||||
<li><strong>POST</strong>: To create new items. In general most item properties are optional. If you omit any, a default value will be used.</li>
|
||||
<li><strong>PUT</strong>: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won't be cleared nor changed).</li>
|
||||
<li><strong>DELETE</strong>: To delete items.</li>
|
||||
</ul>
|
||||
@ -286,6 +286,8 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
<li>All date/time are Unix timestamps in milliseconds.</li>
|
||||
<li>Booleans are integer values 0 or 1.</li>
|
||||
</ul>
|
||||
<h1 id="testing-if-the-service-is-available">Testing if the service is available</h1>
|
||||
<p>Call <strong>GET /ping</strong> to check if the service is available. It should return "JoplinClipperServer" if it works.</p>
|
||||
<h1 id="notes">Notes</h1>
|
||||
<h2 id="properties">Properties</h2>
|
||||
<table>
|
||||
@ -355,7 +357,7 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
<tr>
|
||||
<td>source_url</td>
|
||||
<td>text</td>
|
||||
<td></td>
|
||||
<td>The full URL where the note comes from.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>is_todo</td>
|
||||
@ -412,14 +414,49 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
<td>int</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>body_html</td>
|
||||
<td>text</td>
|
||||
<td>Note body, in HTML format</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>base_url</td>
|
||||
<td>text</td>
|
||||
<td>If <code>body_html</code> is provided and contains relative URLs, provide the <code>base_url</code> parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was <code>https://stackoverflow.com/search?q=%5Bjava%5D+test</code>, the base URL is <code>https://stackoverflow.com/search</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>image_data_url</td>
|
||||
<td>text</td>
|
||||
<td>An image to attach to the note, in <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs">Data URL</a> format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>crop_rect</td>
|
||||
<td>text</td>
|
||||
<td>If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format <code>{ x: x, y: y, width: width, height: height }</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="get-notes">GET /notes</h2>
|
||||
<p>Gets all notes</p>
|
||||
<h2 id="get-notes-id">GET /notes/:id</h2>
|
||||
<p>Gets note with ID :id</p>
|
||||
<h2 id="get-notes-id-tags">GET /notes/:id/tags</h2>
|
||||
<p>Gets all the tags attached to this note.</p>
|
||||
<h2 id="post-notes">POST /notes</h2>
|
||||
<p>Creates a new note</p>
|
||||
<p>You can either specify the note body as Markdown by setting the <code>body</code> parameter, or in HTML by setting the <code>body_html</code>.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li><p>Create a note from some Markdown text</p>
|
||||
<pre><code>curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes
|
||||
</code></pre></li>
|
||||
<li><p>Create a note from some HTML</p>
|
||||
<pre><code>curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes
|
||||
</code></pre></li>
|
||||
<li><p>Create a note and attach an image to it:</p>
|
||||
<pre><code>curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes
|
||||
</code></pre></li>
|
||||
</ul>
|
||||
<h2 id="put-notes-id">PUT /notes/:id</h2>
|
||||
<p>Sets the properties of the note with ID :id</p>
|
||||
<h2 id="delete-notes-id">DELETE /notes/:id</h2>
|
||||
@ -485,8 +522,11 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
</table>
|
||||
<h2 id="get-folders">GET /folders</h2>
|
||||
<p>Gets all folders</p>
|
||||
<p>The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the <code>children</code> key.</p>
|
||||
<h2 id="get-folders-id">GET /folders/:id</h2>
|
||||
<p>Gets folder with ID :id</p>
|
||||
<h2 id="get-folders-id-notes">GET /folders/:id/notes</h2>
|
||||
<p>Gets all the notes inside this folder.</p>
|
||||
<h2 id="post-folders">POST /folders</h2>
|
||||
<p>Creates a new folder</p>
|
||||
<h2 id="put-folders-id">PUT /folders/:id</h2>
|
||||
@ -637,7 +677,7 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
<h2 id="get-tags-id">GET /tags/:id</h2>
|
||||
<p>Gets tag with ID :id</p>
|
||||
<h2 id="get-tags-id-notes">GET /tags/:id/notes</h2>
|
||||
<p>Get all the notes with this tag.</p>
|
||||
<p>Gets all the notes with this tag.</p>
|
||||
<h2 id="post-tags">POST /tags</h2>
|
||||
<p>Creates a new tag</p>
|
||||
<h2 id="post-tags-id-notes">POST /tags/:id/notes</h2>
|
||||
@ -647,7 +687,7 @@ for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
<h2 id="delete-tags-id">DELETE /tags/:id</h2>
|
||||
<p>Deletes the tag with ID :id</p>
|
||||
<h2 id="delete-tags-id-notes-note_id">DELETE /tags/:id/notes/:note_id</h2>
|
||||
<p>Remove the tag from the note..</p>
|
||||
<p>Remove the tag from the note.</p>
|
||||
|
||||
<script>
|
||||
function stickyHeader() {
|
||||
|
@ -282,90 +282,7 @@
|
||||
<p>Now use the extension as normal and replicate the bug you're having.</p>
|
||||
<p>Copy and paste the content of both the debugging window and the Firefox console, and post it to the <a href="https://discourse.joplin.cozic.net/">forum</a>.</p>
|
||||
<h1 id="using-the-web-clipper-service">Using the Web Clipper service</h1>
|
||||
<p>The Web Clipper service can be used to create notes from any other application. It exposes a <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST API</a> with a number of methods to list folders and to create notes or attach images.</p>
|
||||
<p>In order to use it, you'll first need to find on which port the service is running. To do so, open the Web Clipper Option in Joplin and if the service is running it should tell you on which port. Normally it runs on port <strong>41184</strong>. If you want to find it programmatically, you may follow this kind of algorithm:</p>
|
||||
<pre><code class="lang-javascript">let port = null;
|
||||
for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
const result = pingPort(portToTest); // Call GET /ping
|
||||
if (result == 'JoplinClipperServer') {
|
||||
port = portToTest; // Found the port
|
||||
break;
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>The following methods are available:</p>
|
||||
<h3 id="get-ping">GET /ping</h3>
|
||||
<p>Tells whether the service is active or not. It should return "JoplinClipperServer" if it works.</p>
|
||||
<p>Example: <code>curl http://127.0.0.1:41184/ping</code></p>
|
||||
<h3 id="get-folders">GET /folders</h3>
|
||||
<p>Returns the list of notebooks (called "folders" internally) as a tree. The sub-notebooks of a notebook, if any, are under the <code>children</code> key.</p>
|
||||
<p>Example: <code>curl http://127.0.0.1:41184/folders</code></p>
|
||||
<h3 id="post-notes">POST /notes</h3>
|
||||
<p>Creates a new note. You can either specify the note body as Markdown by setting the <code>body</code> parameter, or in HTML by setting the <code>body_html</code>. All parameter are optional.</p>
|
||||
<p>Parameters:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>title</td>
|
||||
<td>Note title</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>body</td>
|
||||
<td>Note body, in Markdown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>body_html</td>
|
||||
<td>Note body, in HTML format</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>source_url</td>
|
||||
<td>The URL the note comes from</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>author</td>
|
||||
<td>The note author</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>parent_id</td>
|
||||
<td>The notebook (ID) to move the note to</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>base_url</td>
|
||||
<td>If <code>body_html</code> is provided and contains relative URLs, provide the <code>base_url</code> parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was <code>https://stackoverflow.com/search?q=%5Bjava%5D+test</code>, the base URL is <code>https://stackoverflow.com/search</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>source_url</td>
|
||||
<td>The <em>full URL</em> of the page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>image_data_url</td>
|
||||
<td>An image to attach to the note, in <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs">Data URL</a> format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>crop_rect</td>
|
||||
<td>If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format <code>{ x: x, y: y, width: width, height: height }</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>Create a note from some Markdown text</li>
|
||||
</ul>
|
||||
<p><code>curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes</code></p>
|
||||
<ul>
|
||||
<li>Create a note from some HTML</li>
|
||||
</ul>
|
||||
<p><code>curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes</code></p>
|
||||
<ul>
|
||||
<li>Create a note and attach an image to it:</li>
|
||||
</ul>
|
||||
<p><code>curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes</code></p>
|
||||
<p>The Web Clipper service can be used to create, modify or delete notes, notebooks, tags, etc. from any other application. It exposes an API with a number of methods to manage Joplin's data. For more information about this API and how to use it, please check the <a href="https://joplin.cozic.net/api">Joplin API documentation</a>.</p>
|
||||
|
||||
<script>
|
||||
function stickyHeader() {
|
||||
|
@ -38,7 +38,7 @@ For example, for the endpoint `DELETE /tags/:id/notes/:note_id`, to remove the t
|
||||
The four verbs supported by the API are the following ones:
|
||||
|
||||
* **GET**: To retrieve items (notes, notebooks, etc.).
|
||||
* **POST**: To create new items.
|
||||
* **POST**: To create new items. In general most item properties are optional. If you omit any, a default value will be used.
|
||||
* **PUT**: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won't be cleared nor changed).
|
||||
* **DELETE**: To delete items.
|
||||
|
||||
@ -48,6 +48,10 @@ The four verbs supported by the API are the following ones:
|
||||
* All date/time are Unix timestamps in milliseconds.
|
||||
* Booleans are integer values 0 or 1.
|
||||
|
||||
# Testing if the service is available
|
||||
|
||||
Call **GET /ping** to check if the service is available. It should return "JoplinClipperServer" if it works.
|
||||
|
||||
# Notes
|
||||
|
||||
## Properties
|
||||
@ -65,7 +69,7 @@ latitude | numeric |
|
||||
longitude | numeric |
|
||||
altitude | numeric |
|
||||
author | text |
|
||||
source_url | text |
|
||||
source_url | text | The full URL where the note comes from.
|
||||
is_todo | int | Tells whether this note is a todo or not.
|
||||
todo_due | int | When the todo is due. An alarm will be triggered on that date.
|
||||
todo_completed | int | Tells whether todo is completed or not. This is a timestamp in milliseconds.
|
||||
@ -77,6 +81,10 @@ user_created_time | int | When the note was created. It may differ from created_
|
||||
user_updated_time | int | When the note was last updated. It may differ from updated_time as it can be manually set by the user.
|
||||
encryption_cipher_text | text |
|
||||
encryption_applied | int |
|
||||
body_html | text | Note body, in HTML format
|
||||
base_url | text | If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`.
|
||||
image_data_url | text | An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
|
||||
crop_rect | text | If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`
|
||||
|
||||
## GET /notes
|
||||
|
||||
@ -86,10 +94,30 @@ Gets all notes
|
||||
|
||||
Gets note with ID :id
|
||||
|
||||
## GET /notes/:id/tags
|
||||
|
||||
Gets all the tags attached to this note.
|
||||
|
||||
## POST /notes
|
||||
|
||||
Creates a new note
|
||||
|
||||
You can either specify the note body as Markdown by setting the `body` parameter, or in HTML by setting the `body_html`.
|
||||
|
||||
Examples:
|
||||
|
||||
* Create a note from some Markdown text
|
||||
|
||||
curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes
|
||||
|
||||
* Create a note from some HTML
|
||||
|
||||
curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes
|
||||
|
||||
* Create a note and attach an image to it:
|
||||
|
||||
curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes
|
||||
|
||||
## PUT /notes/:id
|
||||
|
||||
Sets the properties of the note with ID :id
|
||||
@ -120,10 +148,16 @@ parent_id | text |
|
||||
|
||||
Gets all folders
|
||||
|
||||
The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the `children` key.
|
||||
|
||||
## GET /folders/:id
|
||||
|
||||
Gets folder with ID :id
|
||||
|
||||
## GET /folders/:id/notes
|
||||
|
||||
Gets all the notes inside this folder.
|
||||
|
||||
## POST /folders
|
||||
|
||||
Creates a new folder
|
||||
@ -206,7 +240,7 @@ Gets tag with ID :id
|
||||
|
||||
## GET /tags/:id/notes
|
||||
|
||||
Get all the notes with this tag.
|
||||
Gets all the notes with this tag.
|
||||
|
||||
## POST /tags
|
||||
|
||||
@ -226,5 +260,5 @@ Deletes the tag with ID :id
|
||||
|
||||
## DELETE /tags/:id/notes/:note_id
|
||||
|
||||
Remove the tag from the note..
|
||||
Remove the tag from the note.
|
||||
|
||||
|
@ -43,64 +43,4 @@ Copy and paste the content of both the debugging window and the Firefox console,
|
||||
|
||||
# Using the Web Clipper service
|
||||
|
||||
The Web Clipper service can be used to create notes from any other application. It exposes a [REST API](https://en.wikipedia.org/wiki/Representational_state_transfer) with a number of methods to list folders and to create notes or attach images.
|
||||
|
||||
In order to use it, you'll first need to find on which port the service is running. To do so, open the Web Clipper Option in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:
|
||||
|
||||
```javascript
|
||||
let port = null;
|
||||
for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
|
||||
const result = pingPort(portToTest); // Call GET /ping
|
||||
if (result == 'JoplinClipperServer') {
|
||||
port = portToTest; // Found the port
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following methods are available:
|
||||
|
||||
### GET /ping
|
||||
|
||||
Tells whether the service is active or not. It should return "JoplinClipperServer" if it works.
|
||||
|
||||
Example: `curl http://127.0.0.1:41184/ping`
|
||||
|
||||
### GET /folders
|
||||
|
||||
Returns the list of notebooks (called "folders" internally) as a tree. The sub-notebooks of a notebook, if any, are under the `children` key.
|
||||
|
||||
Example: `curl http://127.0.0.1:41184/folders`
|
||||
|
||||
### POST /notes
|
||||
|
||||
Creates a new note. You can either specify the note body as Markdown by setting the `body` parameter, or in HTML by setting the `body_html`. All parameter are optional.
|
||||
|
||||
Parameters:
|
||||
|
||||
Key | Description
|
||||
---|---
|
||||
title | Note title
|
||||
body | Note body, in Markdown
|
||||
body_html | Note body, in HTML format
|
||||
source_url | The URL the note comes from
|
||||
author | The note author
|
||||
parent_id | The notebook (ID) to move the note to
|
||||
base_url | If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`.
|
||||
source_url | The *full URL* of the page.
|
||||
image_data_url | An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
|
||||
crop_rect | If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`
|
||||
|
||||
Examples:
|
||||
|
||||
* Create a note from some Markdown text
|
||||
|
||||
`curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes`
|
||||
|
||||
* Create a note from some HTML
|
||||
|
||||
`curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes`
|
||||
|
||||
* Create a note and attach an image to it:
|
||||
|
||||
`curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes`
|
||||
The Web Clipper service can be used to create, modify or delete notes, notebooks, tags, etc. from any other application. It exposes an API with a number of methods to manage Joplin's data. For more information about this API and how to use it, please check the [Joplin API documentation](https://joplin.cozic.net/api).
|
Loading…
Reference in New Issue
Block a user