mirror of https://github.com/laurent22/joplin.git
Plugins: Allow updating a resource via the data API
parent
d5dfecc19f
commit
37d51c3b58
|
@ -313,6 +313,10 @@ async function fetchAllNotes() {
|
|||
lines.push('');
|
||||
lines.push('\tcurl -F \'data=@/path/to/file.jpg\' -F \'props={"title":"my resource title"}\' http://localhost:41184/resources');
|
||||
lines.push('');
|
||||
lines.push('Or to **update** a resource:');
|
||||
lines.push('');
|
||||
lines.push('\tcurl -X PUT -F \'data=@/path/to/file.jpg\' -F \'props={"title":"my modified title"}\' http://localhost:41184/resources/8fe1417d7b184324bf6b0122b76c4696');
|
||||
lines.push('');
|
||||
lines.push('The "data" field is required, while the "props" one is not. If not specified, default values will be used.');
|
||||
lines.push('');
|
||||
lines.push('**From a plugin** the syntax to create a resource is also a bit special:');
|
||||
|
@ -368,6 +372,11 @@ async function fetchAllNotes() {
|
|||
lines.push(`Sets the properties of the ${singular} with ID :id`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_RESOURCE) {
|
||||
lines.push('You may also update the file data by specifying a file (See `POST /resources` example).');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push(`## DELETE /${tableName}/:id`);
|
||||
lines.push('');
|
||||
lines.push(`Deletes the ${singular} with ID :id`);
|
||||
|
|
|
@ -325,7 +325,7 @@ describe('services_rest_Api', function() {
|
|||
expect(response.body.indexOf(resource.id) >= 0).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not compress images uploaded through resource api', (async () => {
|
||||
it('should not compress images uploaded through resource API', (async () => {
|
||||
const originalImagePath = `${supportDir}/photo-large.png`;
|
||||
await api.route(RequestMethod.POST, 'resources', null, JSON.stringify({
|
||||
title: 'testing resource',
|
||||
|
@ -345,6 +345,39 @@ describe('services_rest_Api', function() {
|
|||
expect(originalImageSize).toEqual(uploadedImageSize);
|
||||
}));
|
||||
|
||||
it('should update a resource', (async () => {
|
||||
await api.route(RequestMethod.POST, 'resources', null, JSON.stringify({
|
||||
title: 'resource',
|
||||
}), [
|
||||
{
|
||||
path: `${supportDir}/photo.jpg`,
|
||||
},
|
||||
]);
|
||||
|
||||
const resourceV1 = (await Resource.all())[0];
|
||||
|
||||
await msleep(1);
|
||||
|
||||
await api.route(RequestMethod.PUT, `resources/${resourceV1.id}`, null, JSON.stringify({
|
||||
title: 'resource mod',
|
||||
}), [
|
||||
{
|
||||
path: `${supportDir}/photo-large.png`,
|
||||
},
|
||||
]);
|
||||
|
||||
const resourceV2 = (await Resource.all())[0];
|
||||
|
||||
expect(resourceV2.title).toBe('resource mod');
|
||||
expect(resourceV2.mime).toBe('image/png');
|
||||
expect(resourceV2.file_extension).toBe('png');
|
||||
expect(resourceV2.updated_time).toBeGreaterThan(resourceV1.updated_time);
|
||||
expect(resourceV2.created_time).toBe(resourceV1.created_time);
|
||||
expect(resourceV2.size).toBeGreaterThan(resourceV1.size);
|
||||
|
||||
expect(resourceV2.size).toBe((await shim.fsDriver().stat(Resource.fullPath(resourceV2))).size);
|
||||
}));
|
||||
|
||||
it('should delete resources', (async () => {
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
|
|
|
@ -47,13 +47,17 @@ export default async function(request: Request, id: string = null, link: string
|
|||
if (link) throw new ErrorNotFound();
|
||||
}
|
||||
|
||||
if (request.method === RequestMethod.POST) {
|
||||
if (request.method === RequestMethod.POST || request.method === RequestMethod.PUT) {
|
||||
const isUpdate = request.method === RequestMethod.PUT;
|
||||
|
||||
if (!request.files.length) throw new ErrorBadRequest('Resource cannot be created without a file');
|
||||
if (isUpdate && !id) throw new ErrorBadRequest('Missing resource ID');
|
||||
const filePath = request.files[0].path;
|
||||
const defaultProps = request.bodyJson(readonlyProperties('POST'));
|
||||
const defaultProps = request.bodyJson(readonlyProperties(request.method));
|
||||
return shim.createResourceFromPath(filePath, defaultProps, {
|
||||
userSideValidation: true,
|
||||
resizeLargeImages: 'never',
|
||||
destinationResourceId: isUpdate ? id : '',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -221,10 +221,15 @@ function shimInit(options = null) {
|
|||
return true;
|
||||
};
|
||||
|
||||
// This is a bit of an ugly method that's used to both create a new resource
|
||||
// from a file, and update one. To update a resource, pass the
|
||||
// destinationResourceId option. This method is indirectly tested in
|
||||
// Api.test.ts.
|
||||
shim.createResourceFromPath = async function(filePath, defaultProps = null, options = null) {
|
||||
options = Object.assign({
|
||||
resizeLargeImages: 'always', // 'always', 'ask' or 'never'
|
||||
userSideValidation: false,
|
||||
destinationResourceId: '',
|
||||
}, options);
|
||||
|
||||
const readChunk = require('read-chunk');
|
||||
|
@ -236,9 +241,10 @@ function shimInit(options = null) {
|
|||
|
||||
defaultProps = defaultProps ? defaultProps : {};
|
||||
|
||||
const resourceId = defaultProps.id ? defaultProps.id : uuid.create();
|
||||
let resourceId = defaultProps.id ? defaultProps.id : uuid.create();
|
||||
if (options.destinationResourceId) resourceId = options.destinationResourceId;
|
||||
|
||||
const resource = Resource.new();
|
||||
let resource = options.destinationResourceId ? {} : Resource.new();
|
||||
resource.id = resourceId;
|
||||
resource.mime = mimeUtils.fromFilename(filePath);
|
||||
resource.title = basename(filePath);
|
||||
|
@ -281,7 +287,18 @@ function shimInit(options = null) {
|
|||
|
||||
const saveOptions = { isNew: true };
|
||||
if (options.userSideValidation) saveOptions.userSideValidation = true;
|
||||
|
||||
if (options.destinationResourceId) {
|
||||
saveOptions.isNew = false;
|
||||
const tempPath = `${targetPath}.tmp`;
|
||||
await shim.fsDriver().move(targetPath, tempPath);
|
||||
resource = await Resource.save(resource, saveOptions);
|
||||
await Resource.updateResourceBlobContent(resource.id, tempPath);
|
||||
await shim.fsDriver().remove(tempPath);
|
||||
return resource;
|
||||
} else {
|
||||
return Resource.save(resource, saveOptions);
|
||||
}
|
||||
};
|
||||
|
||||
shim.attachFileToNoteBody = async function(noteBody, filePath, position = null, options = null) {
|
||||
|
|
Loading…
Reference in New Issue