diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 8d6e03a0997..8185936b9ce 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -12,6 +12,7 @@ - [Restore] Show source remote and restoration time on a restored VM (PR [#7186](https://github.com/vatesfr/xen-orchestra/pull/7186)) - [Backup/Import] Show disk import status during Incremental Replication or restoration of Incremental Backup (PR [#7171](https://github.com/vatesfr/xen-orchestra/pull/7171)) - [VM Creation] Added ISO option in new VM form when creating from template with a disk [#3464](https://github.com/vatesfr/xen-orchestra/issues/3464) (PR [#7166](https://github.com/vatesfr/xen-orchestra/pull/7166)) +- [REST API] `tags` property can be updated (PR [#7196](https://github.com/vatesfr/xen-orchestra/pull/7196)) ### Bug fixes @@ -43,7 +44,7 @@ - @xen-orchestra/cr-seed-cli major - @xen-orchestra/vmware-explorer patch - xen-api major -- xo-server patch +- xo-server minor - xo-server-netbox minor - xo-vmdk-to-vhd patch - xo-web minor diff --git a/docs/restapi.md b/docs/restapi.md index 63e0a5d1406..e1a53a0ea3b 100644 --- a/docs/restapi.md +++ b/docs/restapi.md @@ -123,7 +123,7 @@ Content-Type: application/x-ndjson ## Properties update -> This feature is restricted to `name_label` and `name_description` at the moment. +> This feature is restricted to `name_label`, `name_description` and `tags` at the moment. ```sh curl \ @@ -135,6 +135,30 @@ curl \ 'https://xo.example.org/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac' ``` +### Collections + +For collection properties, like `tags`, it can be more practical to touch a single item without impacting the others. + +An item can be created with `PUT /` and can be destroyed with `DELETE /`. + +Adding a tag: + +```sh +curl \ +-X PUT \ +-b authenticationToken=KQxQdm2vMiv7jBIK0hgkmgxKzemd8wSJ7ugFGKFkTbs \ +'https://xo.example.org/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac/tags/My%20tag' +``` + +Removing a tag: + +```sh +curl \ + -X DELETE \ + -b authenticationToken=KQxQdm2vMiv7jBIK0hgkmgxKzemd8wSJ7ugFGKFkTbs \ + 'https://xo.example.org/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac/tags/My%20tag' +``` + ## VM and VDI destruction For a VM: diff --git a/packages/xo-server/docs/rest-api.md b/packages/xo-server/docs/rest-api.md index e211eaee841..236884f64d0 100644 --- a/packages/xo-server/docs/rest-api.md +++ b/packages/xo-server/docs/rest-api.md @@ -119,7 +119,7 @@ Content-Type: application/x-ndjson ## Properties update -> This feature is restricted to `name_label` and `name_description` at the moment. +> This feature is restricted to `name_label`, `name_description` and `tags` at the moment. ```sh curl \ @@ -131,6 +131,30 @@ curl \ 'https://xo.company.lan/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac' ``` +### Collections + +For collection properties, like `tags`, it can be more practical to touch a single item without impacting the others. + +An item can be created with `PUT /` and can be destroyed with `DELETE /`. + +Adding a tag: + +```sh +curl \ +-X PUT \ +-b authenticationToken=KQxQdm2vMiv7jBIK0hgkmgxKzemd8wSJ7ugFGKFkTbs \ +'https://xo.company.lan/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac/tags/My%20tag' +``` + +Removing a tag: + +```sh +curl \ + -X DELETE \ + -b authenticationToken=KQxQdm2vMiv7jBIK0hgkmgxKzemd8wSJ7ugFGKFkTbs \ + 'https://xo.company.lan/rest/v0/vms/770aa52a-fd42-8faf-f167-8c5c4a237cac/tags/My%20tag' +``` + ## VM destruction ```sh diff --git a/packages/xo-server/src/xo-mixins/rest-api.mjs b/packages/xo-server/src/xo-mixins/rest-api.mjs index e83ef875ef7..57f4a824fac 100644 --- a/packages/xo-server/src/xo-mixins/rest-api.mjs +++ b/packages/xo-server/src/xo-mixins/rest-api.mjs @@ -479,24 +479,43 @@ export default class RestApi { res.json(result) }) - api.patch( - '/:collection/:object', - json(), - wrap(async (req, res) => { - const obj = req.xapiObject - - const promises = [] - const { body } = req - for (const key of ['name_description', 'name_label']) { - const value = body[key] - if (value !== undefined) { - promises.push(obj['set_' + key](value)) + api + .patch( + '/:collection/:object', + json(), + wrap(async (req, res) => { + const obj = req.xapiObject + + const promises = [] + const { body } = req + + for (const key of ['name_description', 'name_label', 'tags']) { + const value = body[key] + if (value !== undefined) { + promises.push(obj['set_' + key](value)) + } } - } - await promises - res.sendStatus(204) - }) - ) + + await promises + res.sendStatus(204) + }) + ) + .delete( + '/:collection/:object/tags/:tag', + wrap(async (req, res) => { + await req.xapiObject.$call('remove_tags', req.params.tag) + + res.sendStatus(204) + }) + ) + .put( + '/:collection/:object/tags/:tag', + wrap(async (req, res) => { + await req.xapiObject.$call('add_tags', req.params.tag) + + res.sendStatus(204) + }) + ) api.get( '/:collection/:object/tasks',