mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
Compare commits
2 Commits
8025af3cc8
...
feat_sort_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5676dad08d | ||
|
|
4f098e1455 |
@@ -15,7 +15,7 @@ import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs'
|
||||
import type { MaybePromise, SendObjects, WithHref } from '../helpers/helper.type.mjs'
|
||||
import type { Response as ExResponse } from 'express'
|
||||
import { invalidParameters } from 'xo-common/api-errors.js'
|
||||
import { BASE_URL, NDJSON_CONTENT_TYPE, safeParseComplexMatcher } from '../helpers/utils.helper.mjs'
|
||||
import { BASE_URL, NDJSON_CONTENT_TYPE, safeParseComplexMatcher, sortObjects } from '../helpers/utils.helper.mjs'
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
@@ -47,6 +47,9 @@ export abstract class BaseController<T extends XoRecord, IsSync extends boolean>
|
||||
limit?: number
|
||||
}
|
||||
): SendObjects<Objects> {
|
||||
// sorted before the limit, so that the limit keeps the first objects in sort order
|
||||
objects = sortObjects(objects, req.query.sort)
|
||||
|
||||
const mapper = makeObjectMapper(req, opts?.path)
|
||||
const mappedObjects: (string | WithHref<Partial<Objects>>)[] = []
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ export class AclPrivilegeController extends XoController<AnyPrivilege> {
|
||||
* @example fields "id,action,resource"
|
||||
* @example filter "action:create"
|
||||
* @example limit 42
|
||||
* @example sort "action"
|
||||
*/
|
||||
@Example(aclPrivilegeIds)
|
||||
@Example(partialAclPrivileges)
|
||||
@@ -79,7 +80,8 @@ export class AclPrivilegeController extends XoController<AnyPrivilege> {
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<AnyPrivilege>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -76,6 +76,7 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
* @example fields "id,name,isTemplate"
|
||||
* @example filter "name:read only"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(aclRoleIds)
|
||||
@Example(partialAclRoles)
|
||||
@@ -87,7 +88,8 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAclRole>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -300,6 +302,7 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
* @example fields "id,action,resource"
|
||||
* @example filter "action:create"
|
||||
* @example limit 42
|
||||
* @example sort "action"
|
||||
*/
|
||||
@Example(aclPrivilegeIds)
|
||||
@Example(partialAclPrivileges)
|
||||
@@ -313,7 +316,8 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<AnyPrivilege>>> {
|
||||
const privileges = (await this.restApi.xoApp.getAclV2RolePrivileges(id as XoAclRole['id'])) as AnyPrivilege[]
|
||||
return this.sendObjects(limitAndFilterArray(privileges, { filter }), req, {
|
||||
@@ -457,6 +461,7 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
* @example fields "permission,name,id"
|
||||
* @example filter "permission:none"
|
||||
* @example limit 42
|
||||
* @example sort "email"
|
||||
*/
|
||||
@Example(userIds)
|
||||
@Example(partialUsers)
|
||||
@@ -472,7 +477,8 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoUser>>> {
|
||||
const role = await this.getObject(id as XoAclRole['id'])
|
||||
const users =
|
||||
@@ -503,6 +509,7 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
* @example fields "name,id,users"
|
||||
* @example filter "users:length:>0"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(groupIds)
|
||||
@Example(partialGroups)
|
||||
@@ -518,7 +525,8 @@ export class AclRoleController extends XoController<XoAclRole> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoGroup>>> {
|
||||
const role = await this.getObject(id as XoAclRole['id'])
|
||||
const groups =
|
||||
|
||||
@@ -55,6 +55,7 @@ export class AlarmController extends XapiXoController<XoAlarm> {
|
||||
* @example fields "body,id,object"
|
||||
* @example filter "body:name:physical_utilisation"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(alarmIds)
|
||||
@Example(partialAlarms)
|
||||
@@ -67,7 +68,8 @@ export class AlarmController extends XapiXoController<XoAlarm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandedXoAlarm>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -101,6 +101,7 @@ export class BackupArchiveController extends XoController<XoVmBackupArchive> {
|
||||
* @example fields "id,backupRepository,disks"
|
||||
* @example filter "disks:length:>0"
|
||||
* @example limit 42
|
||||
* @example sort "-timestamp"
|
||||
*/
|
||||
@Example(backupArchiveIds)
|
||||
@Example(partialBackupArchives)
|
||||
@@ -115,7 +116,8 @@ export class BackupArchiveController extends XoController<XoVmBackupArchive> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVmBackupArchive>>> {
|
||||
const backupArchives = await this.getObjects({ backupRepositories, filter })
|
||||
return this.sendObjects(Object.values(backupArchives), req, {
|
||||
|
||||
@@ -93,6 +93,7 @@ export class BackupJobController extends XoController<AnyXoBackupJob> {
|
||||
* @example fields "name,mode,type,id"
|
||||
* @example filter "type:backup"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(vmBackupJobIds)
|
||||
@Example(partialVmBackupJobs)
|
||||
@@ -105,7 +106,8 @@ export class BackupJobController extends XoController<AnyXoBackupJob> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandAnyXoBackupJob>> {
|
||||
const backupJobs = await this.getObjects({ filter })
|
||||
return this.sendObjects(Object.values(backupJobs), req, {
|
||||
@@ -201,6 +203,7 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
* @example fields "name,mode,id"
|
||||
* @example filter "mode:delta"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(vmBackupJobIds)
|
||||
@Example(partialVmBackupJobs)
|
||||
@@ -215,7 +218,8 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandXoVmBackupJob>> {
|
||||
const vmBackupJobs = await this.restApi.xoApp.getAllJobs('backup')
|
||||
return this.sendObjects(limitAndFilterArray(vmBackupJobs, { filter }), req, {
|
||||
@@ -255,6 +259,7 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
* @example fields "name,xoMetadata,id"
|
||||
* @example filter "xoMetadata?"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(metadataBackupJobIds)
|
||||
@Example(partialMetadataBackupJobs)
|
||||
@@ -269,7 +274,8 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandXoMetadataBackupJob>> {
|
||||
const metadataBackupJobs = await this.restApi.xoApp.getAllJobs('metadataBackup')
|
||||
return this.sendObjects(limitAndFilterArray(metadataBackupJobs, { filter }), req, {
|
||||
@@ -299,6 +305,7 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
* @example fields "name,mode,id"
|
||||
* @example filter "mode:delta"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(mirrorBackupJobIds)
|
||||
@Example(partialMirrorBackupJobs)
|
||||
@@ -313,7 +320,8 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandXoMirrorBackupJob>> {
|
||||
const mirrorBackupJobs = await this.restApi.xoApp.getAllJobs('mirrorBackup')
|
||||
return this.sendObjects(limitAndFilterArray(mirrorBackupJobs, { filter }), req, {
|
||||
@@ -343,6 +351,7 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
* @example fields "jobName,status,data"
|
||||
* @example filter "status:success"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(backupLogIds)
|
||||
@Example(partialBackupLogs)
|
||||
@@ -357,7 +366,8 @@ export class DeprecatedBackupController extends XoController<AnyXoBackupJob> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoBackupLog>>> {
|
||||
const userFilter = filter === undefined ? () => true : safeParseComplexMatcher(filter).createPredicate()
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export class BackupLogController extends XoController<XoBackupLog> {
|
||||
* @example fields "jobName,status,data"
|
||||
* @example filter "status:success"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(backupLogIds)
|
||||
@Example(partialBackupLogs)
|
||||
@@ -61,7 +62,8 @@ export class BackupLogController extends XoController<XoBackupLog> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoBackupLog>>> {
|
||||
const backupLogs = await this.getObjects({ filter })
|
||||
return this.sendObjects(Object.values(backupLogs), req, {
|
||||
|
||||
@@ -86,6 +86,7 @@ export class BackupRepositoryController extends XoController<XoBackupRepository>
|
||||
* @example fields "id,name,enabled"
|
||||
* @example filter "enabled?"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(backupRepositoryIds)
|
||||
@Example(partialBackupRepositories)
|
||||
@@ -98,7 +99,8 @@ export class BackupRepositoryController extends XoController<XoBackupRepository>
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoBackupRepository>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -71,6 +71,7 @@ export class GroupController extends XoController<XoGroup> {
|
||||
* @example fields "name,id,users"
|
||||
* @example filter "users:length:>0"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(groupIds)
|
||||
@Example(partialGroups)
|
||||
@@ -83,7 +84,8 @@ export class GroupController extends XoController<XoGroup> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoGroup>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -266,6 +268,7 @@ export class GroupController extends XoController<XoGroup> {
|
||||
* @example fields "permission,name,id"
|
||||
* @example filter "permission:none"
|
||||
* @example limit 42
|
||||
* @example sort "email"
|
||||
*/
|
||||
@Example(userIds)
|
||||
@Example(partialUsers)
|
||||
@@ -281,7 +284,8 @@ export class GroupController extends XoController<XoGroup> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoUser>>> {
|
||||
const group = await this.getObject(id as XoGroup['id'])
|
||||
const users = await Promise.all(group.users.map(id => this.restApi.xoApp.getUser(id, { obfuscatePassword: true })))
|
||||
@@ -300,6 +304,7 @@ export class GroupController extends XoController<XoGroup> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -315,7 +320,8 @@ export class GroupController extends XoController<XoGroup> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoGroup['id'], { filter })
|
||||
|
||||
@@ -334,6 +340,7 @@ export class GroupController extends XoController<XoGroup> {
|
||||
* @example fields "id,name,isTemplate"
|
||||
* @example filter "name:read only"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(aclRoleIds)
|
||||
@Example(partialAclRoles)
|
||||
@@ -349,7 +356,8 @@ export class GroupController extends XoController<XoGroup> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<Exclude<XoAclRole, { isTemplate: true }>>>> {
|
||||
const group = await this.getObject(id as XoGroup['id'])
|
||||
const roles = await Promise.all(group.aclRoleIds.map(roleId => this.restApi.xoApp.getAclV2Role(roleId)))
|
||||
|
||||
@@ -142,15 +142,68 @@ export function safeParseComplexMatcher(string: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const SORT_REGEX = /^(-?)([\w$]+)$/
|
||||
|
||||
/**
|
||||
* Sorts on a single top-level property, `-` prefix for descending order.
|
||||
*
|
||||
* - stable: objects with equal values keep their original order
|
||||
* - objects without the property (`undefined` or `null`) are always last
|
||||
* - strings are compared by code unit (case sensitive, no locale)
|
||||
*/
|
||||
export function sortObjects<T>(array: T[], sort: unknown): T[] {
|
||||
if (sort === undefined) {
|
||||
return array
|
||||
}
|
||||
|
||||
const match = typeof sort === 'string' ? SORT_REGEX.exec(sort) : null
|
||||
if (match === null) {
|
||||
throw new ApiError('sort must be a property name, optionally prefixed by "-" for descending order', 400, {
|
||||
data: { sort },
|
||||
})
|
||||
}
|
||||
const [, sign, property] = match
|
||||
const direction = sign === '-' ? -1 : 1
|
||||
|
||||
return array.toSorted((a, b) => {
|
||||
const aValue = (a as Record<string, unknown>)[property]
|
||||
const bValue = (b as Record<string, unknown>)[property]
|
||||
|
||||
const aIsMissing = aValue === undefined || aValue === null
|
||||
const bIsMissing = bValue === undefined || bValue === null
|
||||
// missing values are always last, whatever the direction
|
||||
if (aIsMissing && bIsMissing) {
|
||||
return 0
|
||||
}
|
||||
if (aIsMissing) {
|
||||
return 1
|
||||
}
|
||||
if (bIsMissing) {
|
||||
return -1
|
||||
}
|
||||
|
||||
// values of different types are considered equal, the stable sort keeps their order
|
||||
if ((aValue as string) < (bValue as string)) {
|
||||
return -direction
|
||||
}
|
||||
if ((aValue as string) > (bValue as string)) {
|
||||
return direction
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
export function limitAndFilterArray<T>(
|
||||
array: T[],
|
||||
{ filter, limit = Infinity }: { filter?: string | ((obj: T) => boolean); limit?: number } = {}
|
||||
{ filter, sort, limit = Infinity }: { filter?: string | ((obj: T) => boolean); sort?: string; limit?: number } = {}
|
||||
): T[] {
|
||||
if (filter !== undefined) {
|
||||
const predicate = typeof filter === 'string' ? safeParseComplexMatcher(filter).createPredicate() : filter
|
||||
array = array.filter(predicate)
|
||||
}
|
||||
|
||||
array = sortObjects(array, sort)
|
||||
|
||||
if (limit < array.length) {
|
||||
array = array.slice(0, limit)
|
||||
}
|
||||
|
||||
76
@xen-orchestra/rest-api/src/helpers/utils.helper.test.mts
Normal file
76
@xen-orchestra/rest-api/src/helpers/utils.helper.test.mts
Normal file
@@ -0,0 +1,76 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { describe, it } from 'node:test'
|
||||
|
||||
import { ApiError } from './error.helper.mjs'
|
||||
import { limitAndFilterArray, sortObjects } from './utils.helper.mjs'
|
||||
|
||||
const ids = (array: { id: string }[]) => array.map(_ => _.id)
|
||||
|
||||
describe('sortObjects()', () => {
|
||||
const objects = [
|
||||
{ id: 'a', name: 'charlie', size: 10 },
|
||||
{ id: 'b', name: 'alpha', size: 2 },
|
||||
{ id: 'c', size: 5 },
|
||||
{ id: 'd', name: 'bravo', size: 10 },
|
||||
{ id: 'e', name: null, size: 1 },
|
||||
]
|
||||
|
||||
it('returns the same array when sort is undefined', () => {
|
||||
assert.equal(sortObjects(objects, undefined), objects)
|
||||
})
|
||||
|
||||
it('does not mutate the original array', () => {
|
||||
const original = ids(objects)
|
||||
sortObjects(objects, 'name')
|
||||
assert.deepEqual(ids(objects), original)
|
||||
})
|
||||
|
||||
it('sorts strings in ascending order', () => {
|
||||
assert.deepEqual(ids(sortObjects(objects, 'name')), ['b', 'd', 'a', 'c', 'e'])
|
||||
})
|
||||
|
||||
it('sorts in descending order with the "-" prefix', () => {
|
||||
assert.deepEqual(ids(sortObjects(objects, '-name')), ['a', 'd', 'b', 'c', 'e'])
|
||||
})
|
||||
|
||||
it('sorts numbers numerically', () => {
|
||||
assert.deepEqual(ids(sortObjects(objects, 'size')), ['e', 'b', 'c', 'a', 'd'])
|
||||
})
|
||||
|
||||
it('keeps the original order of equal values, in both directions', () => {
|
||||
assert.deepEqual(ids(sortObjects(objects, '-size')), ['a', 'd', 'c', 'b', 'e'])
|
||||
})
|
||||
|
||||
it('keeps the original order when no object has the property', () => {
|
||||
assert.deepEqual(ids(sortObjects(objects, 'unknown')), ids(objects))
|
||||
})
|
||||
|
||||
it('accepts property names with "$"', () => {
|
||||
const pools = [
|
||||
{ id: 'a', $pool: 'p2' },
|
||||
{ id: 'b', $pool: 'p1' },
|
||||
]
|
||||
assert.deepEqual(ids(sortObjects(pools, '$pool')), ['b', 'a'])
|
||||
})
|
||||
|
||||
for (const sort of ['', '-', 'a.b', 'name,size', ' name', ['name'], 42]) {
|
||||
it(`throws a 400 for ${JSON.stringify(sort)}`, () => {
|
||||
assert.throws(
|
||||
() => sortObjects(objects, sort),
|
||||
(error: unknown) => error instanceof ApiError && error.status === 400
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('limitAndFilterArray()', () => {
|
||||
it('filters, then sorts, then limits', () => {
|
||||
const objects = [
|
||||
{ id: 'a', size: 3 },
|
||||
{ id: 'b', size: 1 },
|
||||
{ id: 'c', size: 4 },
|
||||
{ id: 'd', size: 2 },
|
||||
]
|
||||
assert.deepEqual(ids(limitAndFilterArray(objects, { filter: 'size:>1', sort: '-size', limit: 2 })), ['c', 'a'])
|
||||
})
|
||||
})
|
||||
@@ -106,6 +106,7 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
* @example fields "id,name_label,productBrand"
|
||||
* @example filter "productBrand:XCP-ng"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(hostIds)
|
||||
@Example(partialHosts)
|
||||
@@ -118,7 +119,8 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoHost>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -232,6 +234,7 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -246,7 +249,8 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const host = this.getObject(id as XoHost['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -312,6 +316,7 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:PBD_PLUG_FAILED_ON_SERVER_START"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -327,7 +332,8 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoHost['id'], { filter })
|
||||
|
||||
@@ -346,6 +352,7 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -361,7 +368,8 @@ export class HostController extends XapiXoController<XoHost> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoHost['id'], { filter })
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ export class MessageController extends XapiXoController<XoMessage> {
|
||||
* @example fields "name,body,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -85,7 +86,8 @@ export class MessageController extends XapiXoController<XoMessage> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandedXoMessage>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -58,6 +58,7 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
* @example fields "nbd,name_label,id"
|
||||
* @example filter "nbd?"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(networkIds)
|
||||
@Example(partialNetworks)
|
||||
@@ -70,7 +71,8 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoNetwork>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -119,6 +121,7 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -133,7 +136,8 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const network = this.getObject(id as XoNetwork['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -155,6 +159,7 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -170,7 +175,8 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoNetwork['id'], { filter })
|
||||
|
||||
@@ -189,6 +195,7 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -204,7 +211,8 @@ export class NetworkController extends XapiXoController<XoNetwork> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoNetwork['id'], { filter })
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ export class PbdController extends XapiXoController<XoPbd> {
|
||||
* @example fields "attached,id,device_config"
|
||||
* @example filter "attached?"
|
||||
* @example limit 42
|
||||
* @example sort "attached"
|
||||
*/
|
||||
@Example(pbdIds)
|
||||
@Example(partialPbds)
|
||||
@@ -67,7 +68,8 @@ export class PbdController extends XapiXoController<XoPbd> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoPbd>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -35,6 +35,7 @@ export class PciController extends XapiXoController<XoPci> {
|
||||
* @example fields "class_name,device_name,id"
|
||||
* @example filter "class_name:Non-Volatile memory controller"
|
||||
* @example limit 42
|
||||
* @example sort "device_name"
|
||||
*/
|
||||
@Example(pciIds)
|
||||
@Example(partialPcis)
|
||||
@@ -47,7 +48,8 @@ export class PciController extends XapiXoController<XoPci> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoPci>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -35,6 +35,7 @@ export class PgpuController extends XapiXoController<XoPgpu> {
|
||||
* @example fields "id,dom0Access,gpuGroup"
|
||||
* @example filter "dom0Access:enabled"
|
||||
* @example limit 42
|
||||
* @example sort "host"
|
||||
*/
|
||||
@Example(pgpuIds)
|
||||
@Example(partialPgpus)
|
||||
@@ -47,7 +48,8 @@ export class PgpuController extends XapiXoController<XoPgpu> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoPgpu>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -44,6 +44,7 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
* @example fields "attached,device,deviceName,id"
|
||||
* @example filter "attached?"
|
||||
* @example limit 42
|
||||
* @example sort "device"
|
||||
*/
|
||||
@Example(pifIds)
|
||||
@Example(partialPifs)
|
||||
@@ -56,7 +57,8 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandedXoPif>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -88,6 +90,7 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -102,7 +105,8 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const pif = this.getObject(id as XoPif['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -124,6 +128,7 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -139,7 +144,8 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoPif['id'], { filter })
|
||||
|
||||
@@ -158,6 +164,7 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -173,7 +180,8 @@ export class PifController extends XapiXoController<XoPif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoPif['id'], { filter })
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ export class PluginController extends XoController<XoPlugin> {
|
||||
* @example fields "id,name,loaded,autoload"
|
||||
* @example filter "loaded?"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(pluginIds)
|
||||
@Example(partialPlugins)
|
||||
@@ -90,7 +91,8 @@ export class PluginController extends XoController<XoPlugin> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoPlugin>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -129,6 +129,7 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
* @example fields "auto_poweron,name_label,id"
|
||||
* @example filter "auto_poweron?"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(poolIds)
|
||||
@Example(partialPools)
|
||||
@@ -141,7 +142,8 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoPool>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -671,6 +673,7 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -685,7 +688,8 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const pool = this.getObject(id as XoPool['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -800,6 +804,7 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:IP_CONFIGURED_PIF_CAN_UNPLUG"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -815,7 +820,8 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoPool['id'], { filter })
|
||||
|
||||
@@ -869,6 +875,7 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -884,7 +891,8 @@ export class PoolController extends XapiXoController<XoPool> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoPool['id'], { filter })
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export class ProxyController extends XoController<XoProxy> {
|
||||
* @example fields "vmUuid,id,name"
|
||||
* @example filter "vmUuid?"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(proxyIds)
|
||||
@Example(partialProxies)
|
||||
@@ -54,7 +55,8 @@ export class ProxyController extends XoController<XoProxy> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoProxy>>> {
|
||||
const proxies = Object.values(await this.getObjects({ filter }))
|
||||
return this.sendObjects(proxies, req, {
|
||||
|
||||
@@ -64,6 +64,7 @@ export class RestoreLogController extends XoController<XoRestoreLog> {
|
||||
* @example fields "jobName,status,data"
|
||||
* @example filter "status:success"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(restoreLogIds)
|
||||
@Example(partialRestoreLogs)
|
||||
@@ -76,7 +77,8 @@ export class RestoreLogController extends XoController<XoRestoreLog> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): Promise<SendObjects<Partial<Unbrand<XoRestoreLog>>>> {
|
||||
const restoreLogs = await this.getObjects({ filter })
|
||||
return this.sendObjects(Object.values(restoreLogs), req, {
|
||||
@@ -146,6 +148,7 @@ export class DeprecatedRestoreController extends XoController<XoRestoreLog> {
|
||||
* @example fields "jobName,status,data"
|
||||
* @example filter "status:success"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(restoreLogIds)
|
||||
@Example(partialRestoreLogs)
|
||||
@@ -158,7 +161,8 @@ export class DeprecatedRestoreController extends XoController<XoRestoreLog> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoRestoreLog>>> {
|
||||
const restoreLogs = await this.getObjects({ filter })
|
||||
return this.sendObjects(Object.values(restoreLogs), req, {
|
||||
|
||||
@@ -63,6 +63,7 @@ export class ScheduleController extends XoController<XoSchedule> {
|
||||
* @example fields "enabled,jobId,cron,id"
|
||||
* @example filter "enabled?"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(scheduleIds)
|
||||
@Example(partialSchedules)
|
||||
@@ -75,7 +76,8 @@ export class ScheduleController extends XoController<XoSchedule> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoSchedule>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -69,6 +69,7 @@ export class ServerController extends XoController<XoServer> {
|
||||
* @example fields "status,id"
|
||||
* @example filter "status:/^connected$/"
|
||||
* @example limit 42
|
||||
* @example sort "label"
|
||||
*/
|
||||
@Example(serverIds)
|
||||
@Example(partialServers)
|
||||
@@ -81,7 +82,8 @@ export class ServerController extends XoController<XoServer> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): Promise<SendObjects<Partial<Unbrand<XoServer>>>> {
|
||||
return this.sendObjects(Object.values(await this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -239,6 +241,7 @@ export class ServerController extends XoController<XoServer> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -254,7 +257,8 @@ export class ServerController extends XoController<XoServer> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoServer['id'], { filter })
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export class SmController extends XapiXoController<XoSm> {
|
||||
* @example fields "uuid,name_label,SM_type"
|
||||
* @example filter "SM_type:ext"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(smIds)
|
||||
@Example(partialSms)
|
||||
@@ -41,7 +42,8 @@ export class SmController extends XapiXoController<XoSm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoSm>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
|
||||
@@ -74,6 +74,7 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
* @example fields "uuid,name_label,allocationStrategy"
|
||||
* @example filter "allocationStrategy:thin"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(srIds)
|
||||
@Example(partialSrs)
|
||||
@@ -86,7 +87,8 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoSr>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -150,6 +152,7 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -164,7 +167,8 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const sr = this.getObject(id as XoSr['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -231,6 +235,7 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -246,7 +251,8 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoSr['id'], { filter })
|
||||
|
||||
@@ -265,6 +271,7 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -280,7 +287,8 @@ export class SrController extends XapiXoController<XoSr> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoSr['id'], { filter })
|
||||
return this.sendObjects(Object.values(tasks), req, {
|
||||
|
||||
@@ -75,6 +75,7 @@ export class TaskController extends XoController<XoTask> {
|
||||
* @example fields "status,id,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -89,7 +90,8 @@ export class TaskController extends XoController<XoTask> {
|
||||
@Query() markdown?: boolean,
|
||||
@Query() watch?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
if (watch) {
|
||||
if (!ndjson) {
|
||||
|
||||
@@ -90,6 +90,7 @@ export class UserController extends XoController<XoUser> {
|
||||
* @example fields "permission,name,id"
|
||||
* @example filter "permission:none"
|
||||
* @example limit 42
|
||||
* @example sort "email"
|
||||
*/
|
||||
@Example(userIds)
|
||||
@Example(partialUsers)
|
||||
@@ -102,7 +103,8 @@ export class UserController extends XoController<XoUser> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoUser>>> {
|
||||
const users = Object.values(await this.getObjects({ filter }))
|
||||
return this.sendObjects(users, req, {
|
||||
@@ -266,6 +268,7 @@ export class UserController extends XoController<XoUser> {
|
||||
* @example fields "name,id,users"
|
||||
* @example filter "users:length:>0"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(groupIds)
|
||||
@Example(partialGroups)
|
||||
@@ -281,7 +284,8 @@ export class UserController extends XoController<XoUser> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoGroup>>> {
|
||||
const user = await this.getObject(id as XoUser['id'])
|
||||
const groups = await Promise.all(user.groups.map(group => this.restApi.xoApp.getGroup(group)))
|
||||
@@ -299,6 +303,7 @@ export class UserController extends XoController<XoUser> {
|
||||
* @example id "722d17b9-699b-49d2-8193-be1ac573d3de"
|
||||
* @example filter "expiration:>1757371582496"
|
||||
* @example limit 42
|
||||
* @example sort "expiration"
|
||||
*/
|
||||
@Example(authenticationTokens)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -310,7 +315,8 @@ export class UserController extends XoController<XoUser> {
|
||||
@Request() req: ExRequest,
|
||||
@Path() id: string,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): Promise<Unbrand<XoAuthenticationToken>[]> {
|
||||
const user = await this.getObject(id as XoUser['id'])
|
||||
|
||||
@@ -321,7 +327,7 @@ export class UserController extends XoController<XoUser> {
|
||||
|
||||
const tokens = await this.restApi.xoApp.getAuthenticationTokensForUser(user.id)
|
||||
|
||||
return limitAndFilterArray(tokens, { filter, limit })
|
||||
return limitAndFilterArray(tokens, { filter, sort, limit })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,6 +338,7 @@ export class UserController extends XoController<XoUser> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -347,7 +354,8 @@ export class UserController extends XoController<XoUser> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoUser['id'], { filter })
|
||||
|
||||
@@ -439,6 +447,7 @@ export class UserController extends XoController<XoUser> {
|
||||
* @example fields "id,action,resource"
|
||||
* @example filter "action:create"
|
||||
* @example limit 42
|
||||
* @example sort "action"
|
||||
*/
|
||||
@Example(aclPrivilegeIds)
|
||||
@Example(partialAclPrivileges)
|
||||
@@ -453,7 +462,8 @@ export class UserController extends XoController<XoUser> {
|
||||
@Query() fields?: string,
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<AnyPrivilege>>> {
|
||||
const user = await this.getObject(id as XoUser['id'])
|
||||
const currentUser = this.restApi.getCurrentUser()
|
||||
|
||||
@@ -66,6 +66,7 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
* @example fields "device,bootable,uuid"
|
||||
* @example filter "!bootable?"
|
||||
* @example limit 42
|
||||
* @example sort "position"
|
||||
*/
|
||||
@Example(vbdIds)
|
||||
@Example(partialVbds)
|
||||
@@ -78,7 +79,8 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVbd>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -175,6 +177,7 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -189,7 +192,8 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vbd = this.getObject(id as XoVbd['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -211,6 +215,7 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -226,7 +231,8 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVbd['id'], { filter })
|
||||
|
||||
@@ -245,6 +251,7 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -260,7 +267,8 @@ export class VbdController extends XapiXoController<XoVbd> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVbd['id'], { filter })
|
||||
return this.sendObjects(Object.values(tasks), req, {
|
||||
|
||||
@@ -66,6 +66,7 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
* @example fields "uuid,snapshot_time,$snapshot_of"
|
||||
* @example filter "snapshot_time:>1725020038"
|
||||
* @example limit 42
|
||||
* @example sort "-snapshot_time"
|
||||
*/
|
||||
@Example(vdiSnapshotIds)
|
||||
@Example(partialVdiSnapshots)
|
||||
@@ -78,7 +79,8 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdiSnapshot>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -144,6 +146,7 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -158,7 +161,8 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vdiSnapshot = this.getObject(id as XoVdiSnapshot['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -197,6 +201,7 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -212,7 +217,8 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVdiSnapshot['id'], { filter })
|
||||
|
||||
@@ -231,6 +237,7 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -246,7 +253,8 @@ export class VdiSnapshotController extends XapiXoController<XoVdiSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVdiSnapshot['id'], { filter })
|
||||
return this.sendObjects(Object.values(tasks), req, {
|
||||
|
||||
@@ -102,6 +102,7 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
* @example fields "*"
|
||||
* @example filter "snapshots:length:>2"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vdiIds)
|
||||
@Example(partialVdis)
|
||||
@@ -114,7 +115,8 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdi>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -261,6 +263,7 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -275,7 +278,8 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vdi = this.getObject(id as XoVdi['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -359,6 +363,7 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -374,7 +379,8 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVdi['id'], { filter })
|
||||
|
||||
@@ -393,6 +399,7 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -408,7 +415,8 @@ export class VdiController extends XapiXoController<XoVdi> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVdi['id'], { filter })
|
||||
return this.sendObjects(Object.values(tasks), req, {
|
||||
|
||||
@@ -99,6 +99,7 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
* @example fields "attached,id,device"
|
||||
* @example filter "attached?"
|
||||
* @example limit 42
|
||||
* @example sort "device"
|
||||
*/
|
||||
@Example(vifIds)
|
||||
@Example(partialVifs)
|
||||
@@ -111,7 +112,8 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandedXoVif>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -201,6 +203,7 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -215,7 +218,8 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vif = this.getObject(id as XoVif['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -237,6 +241,7 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -252,7 +257,8 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVif['id'], { filter })
|
||||
|
||||
@@ -271,6 +277,7 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -286,7 +293,8 @@ export class VifController extends XapiXoController<XoVif> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVif['id'], { filter })
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
* @example fields "type,uuid"
|
||||
* @example filter "power_state:Running"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmControllerIds)
|
||||
@Example(partialVmControllers)
|
||||
@@ -84,7 +85,8 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVmController>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -116,6 +118,7 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -130,7 +133,8 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vmController = this.getObject(id as XoVmController['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -152,6 +156,7 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
* @example fields "VDI_type,id,name_label"
|
||||
* @example filter "VDI_type:user"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmControllerVdis)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -166,7 +171,8 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdi>>> {
|
||||
const vdis = this.#vmService.getVmVdis(id as XoVmController['id'], 'VM-controller')
|
||||
return this.sendObjects(limitAndFilterArray(vdis, { filter }), req, {
|
||||
@@ -184,6 +190,7 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -199,7 +206,8 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVmController['id'], { filter })
|
||||
|
||||
@@ -218,6 +226,7 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -233,7 +242,8 @@ export class VmControllerController extends XapiXoController<XoVmController> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVmController['id'], { filter })
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
* @example fields "uuid,snapshot_time,$snapshot_of"
|
||||
* @example filter "snapshot_time:>1725020038"
|
||||
* @example limit 42
|
||||
* @example sort "-snapshot_time"
|
||||
*/
|
||||
@Example(vmSnapshotIds)
|
||||
@Example(partialVmSnapshots)
|
||||
@@ -86,7 +87,8 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVmSnapshot>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -169,6 +171,7 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -183,7 +186,8 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vmSnapshot = this.getObject(id as XoVmSnapshot['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -205,6 +209,7 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
* @example fields "VDI_type,id,name_label"
|
||||
* @example filter "VDI_type:user"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmSnapshotVdis)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -219,7 +224,8 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdiSnapshot>>> {
|
||||
const vdis = this.#vmService.getVmVdis(id as XoVmSnapshot['id'], 'VM-snapshot')
|
||||
return this.sendObjects(limitAndFilterArray(vdis, { filter }), req, {
|
||||
@@ -237,6 +243,7 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -252,7 +259,8 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVmSnapshot['id'], { filter })
|
||||
|
||||
@@ -271,6 +279,7 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -286,7 +295,8 @@ export class VmSnapshotController extends XapiXoController<XoVmSnapshot> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVmSnapshot['id'], { filter })
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
* @example fields "id,isDefaultTemplate,name_label"
|
||||
* @example filter "isDefaultTemplate?"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
* */
|
||||
@Example(vmTemplateIds)
|
||||
@Example(partialVmTemplates)
|
||||
@@ -87,7 +88,8 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVmTemplate>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -170,6 +172,7 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -184,7 +187,8 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const vmTemplate = this.getObject(id as XoVmTemplate['id'])
|
||||
const alarms = this.#alarmService.getAlarms({
|
||||
@@ -206,6 +210,7 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
* @example fields "VDI_type,id,name_label"
|
||||
* @example filter "VDI_type:user"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmTemplateVdis)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -220,7 +225,8 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdi>>> {
|
||||
const vdis = this.#vmService.getVmVdis(id as XoVmTemplate['id'], 'VM-template')
|
||||
return this.sendObjects(limitAndFilterArray(vdis, { filter }), req, {
|
||||
@@ -238,6 +244,7 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -253,7 +260,8 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVmTemplate['id'], { filter })
|
||||
|
||||
@@ -272,6 +280,7 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -287,7 +296,8 @@ export class VmTemplateController extends XapiXoController<XoVmTemplate> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVmTemplate['id'], { filter })
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "name_label,power_state,uuid"
|
||||
* @example filter "power_state:Running"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmIds)
|
||||
@Example(partialVms)
|
||||
@@ -121,7 +122,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVm>>> {
|
||||
return this.sendObjects(Object.values(this.getObjects({ filter })), req, {
|
||||
limit,
|
||||
@@ -824,6 +826,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "id,time"
|
||||
* @example filter "time:>1747053793"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(genericAlarmsExample)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -838,7 +841,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoAlarm>>> {
|
||||
const alarms = this.#vmService.getVmAlarms(id as XoVm['id'], { filter })
|
||||
|
||||
@@ -857,6 +861,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "VDI_type,id,name_label"
|
||||
* @example filter "VDI_type:user"
|
||||
* @example limit 42
|
||||
* @example sort "name_label"
|
||||
*/
|
||||
@Example(vmVdis)
|
||||
@Extension('x-mcp-exposure', 'allow')
|
||||
@@ -871,7 +876,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoVdi>>> {
|
||||
const vdis = this.#vmService.getVmVdis(id as XoVm['id'], 'VM')
|
||||
return this.sendObjects(limitAndFilterArray(vdis, { filter }), req, {
|
||||
@@ -889,6 +895,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "mode,name,type,id"
|
||||
* @example filter "mode:full"
|
||||
* @example limit 42
|
||||
* @example sort "name"
|
||||
*/
|
||||
@Example(vmBackupJobIds)
|
||||
@Example(partialVmBackupJobs)
|
||||
@@ -904,7 +911,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<UnbrandXoVmBackupJob>> {
|
||||
const backupJobs = await this.restApi.xoApp.getAllJobs('backup')
|
||||
|
||||
@@ -930,6 +938,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "name,id,$object"
|
||||
* @example filter "name:VM_STARTED"
|
||||
* @example limit 42
|
||||
* @example sort "-time"
|
||||
*/
|
||||
@Example(messageIds)
|
||||
@Example(partialMessages)
|
||||
@@ -945,7 +954,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoMessage>>> {
|
||||
const messages = this.getMessagesForObject(id as XoVm['id'], { filter })
|
||||
|
||||
@@ -964,6 +974,7 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
* @example fields "id,status,properties"
|
||||
* @example filter "status:failure"
|
||||
* @example limit 42
|
||||
* @example sort "-start"
|
||||
*/
|
||||
@Example(taskIds)
|
||||
@Example(partialTasks)
|
||||
@@ -979,7 +990,8 @@ export class VmController extends XapiXoController<XoVm> {
|
||||
@Query() ndjson?: boolean,
|
||||
@Query() markdown?: boolean,
|
||||
@Query() filter?: string,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() sort?: string
|
||||
): SendObjects<Partial<Unbrand<XoTask>>> {
|
||||
const tasks = await this.getTasksForObject(id as XoVm['id'], { filter })
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
> Users must be able to say: "Nice enhancement, I'm eager to test it"
|
||||
|
||||
- [REST API] Collections now support a `sort` parameter (e.g. `?sort=-start&limit=10`), applied before `limit`: a single property, prefixed with `-` for descending order, objects without the property are listed last (PR [#](https://github.com/vatesfr/xen-orchestra/pull/))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
> Users must be able to say: "I had this issue, happy to know it's fixed"
|
||||
@@ -31,6 +33,7 @@
|
||||
|
||||
<!--packages-start-->
|
||||
|
||||
- @xen-orchestra/rest-api minor
|
||||
- @xen-orchestra/web-core minor
|
||||
|
||||
<!--packages-end-->
|
||||
|
||||
Reference in New Issue
Block a user