@xen-orchestra/rest-api 0.21.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- import * as CM from 'complex-matcher';
2
1
  import { Controller } from 'tsoa';
3
2
  import { createGzip } from 'node:zlib';
4
3
  import { pipeline } from 'node:stream/promises';
@@ -6,7 +5,7 @@ import { Readable } from 'node:stream';
6
5
  import { BASE_URL } from '../index.mjs';
7
6
  import { makeNdJsonStream } from '../helpers/stream.helper.mjs';
8
7
  import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs';
9
- import { NDJSON_CONTENT_TYPE } from '../helpers/utils.helper.mjs';
8
+ import { NDJSON_CONTENT_TYPE, safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
10
9
  const noop = () => { };
11
10
  export class BaseController extends Controller {
12
11
  restApi;
@@ -34,7 +33,7 @@ export class BaseController extends Controller {
34
33
  const objectFilter = (task) => task.properties.objectId === object.id || task.properties.params?.id === object.id;
35
34
  let userFilter = () => true;
36
35
  if (filter !== undefined) {
37
- userFilter = typeof filter === 'string' ? CM.parse(filter).createPredicate() : filter;
36
+ userFilter = typeof filter === 'string' ? safeParseComplexMatcher(filter).createPredicate() : filter;
38
37
  }
39
38
  for await (const task of this.restApi.tasks.list({ filter: objectFilter })) {
40
39
  if (limit === 0) {
@@ -1,10 +1,10 @@
1
- import * as CM from 'complex-matcher';
2
1
  import { BASE_URL } from '../index.mjs';
2
+ import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
3
3
  // E.g: 'value: 0.6\nconfig:\n<variable>\n<name value="cpu_usage"/>\n<alarm_trigger_level value="0.4"/>\n<alarm_trigger_period value ="60"/>\n</variable>';
4
4
  const ALARM_BODY_REGEX = /^value:\s*(Infinity|NaN|-Infinity|\d+(?:\.\d+)?)\s*config:\s*<variable>\s*<name value="(.*?)"/;
5
5
  const ALARM_NAMES = ['ALARM', 'BOND_STATUS_CHANGED', 'MULTIPATH_PERIODIC_ALERT'];
6
6
  export const RAW_ALARM_FILTER = `name:|(${ALARM_NAMES.join(' ')})`;
7
- export const alarmPredicate = CM.parse(RAW_ALARM_FILTER).createPredicate();
7
+ export const alarmPredicate = safeParseComplexMatcher(RAW_ALARM_FILTER).createPredicate();
8
8
  export class AlarmService {
9
9
  #restApi;
10
10
  constructor(restApi) {
@@ -50,7 +50,7 @@ export class AlarmService {
50
50
  });
51
51
  let userFilter = () => true;
52
52
  if (filter !== undefined) {
53
- userFilter = typeof filter === 'string' ? CM.parse(filter).createPredicate() : filter;
53
+ userFilter = typeof filter === 'string' ? safeParseComplexMatcher(filter).createPredicate() : filter;
54
54
  }
55
55
  const alarms = {};
56
56
  for (const id in rawAlarms) {
@@ -30,6 +30,7 @@ let BackupArchiveController = class BackupArchiveController extends XoController
30
30
  }
31
31
  const backupArchivesByRemote = await this.restApi.xoApp.listVmBackupsNg(backupRepositoryIds);
32
32
  const vmBackupArchives = Object.values(backupArchivesByRemote)
33
+ .filter(backupsByVm => backupsByVm !== undefined)
33
34
  .map(backupsByVm => Object.values(backupsByVm))
34
35
  .flat(2);
35
36
  return vmBackupArchives;
@@ -7,7 +7,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import * as CM from 'complex-matcher';
11
10
  import { createLogger } from '@xen-orchestra/log';
12
11
  import { inject } from 'inversify';
13
12
  import { noSuchObject } from 'xo-common/api-errors.js';
@@ -17,7 +16,7 @@ import { backupLog, backupLogIds, partialBackupLogs } from '../open-api/oa-examp
17
16
  import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
18
17
  import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
19
18
  import { RestApi } from '../rest-api/rest-api.mjs';
20
- import { limitAndFilterArray } from '../helpers/utils.helper.mjs';
19
+ import { limitAndFilterArray, safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
21
20
  import { XoController } from '../abstract-classes/xo-controller.mjs';
22
21
  import { metadataBackupJob, metadataBackupJobIds, mirrorBackupJob, mirrorBackupJobIds, partialMetadataBackupJobs, partialMirrorBackupJobs, partialVmBackupJobs, vmBackupJob, vmBackupJobIds, } from '../open-api/oa-examples/backup-job.oa-example.mjs';
23
22
  import { BASE_URL } from '../index.mjs';
@@ -171,7 +170,7 @@ let DeprecatedBackupController = class DeprecatedBackupController extends XoCont
171
170
  * @example limit 42
172
171
  */
173
172
  async getDeprecatedBackupLogs(req, fields, ndjson, filter, limit) {
174
- const userFilter = filter === undefined ? () => true : CM.parse(filter).createPredicate();
173
+ const userFilter = filter === undefined ? () => true : safeParseComplexMatcher(filter).createPredicate();
175
174
  const predicate = (log) => {
176
175
  if (!this.#backupLogService.isBackupLog(log)) {
177
176
  return false;
@@ -1,10 +1,15 @@
1
1
  export class ApiError extends Error {
2
2
  #status;
3
- constructor(message, status) {
3
+ #data;
4
+ constructor(message, status, opts = {}) {
4
5
  super(message);
5
6
  this.#status = status;
7
+ this.#data = opts.data;
6
8
  }
7
9
  get status() {
8
10
  return this.#status;
9
11
  }
12
+ get data() {
13
+ return this.#data;
14
+ }
10
15
  }
@@ -1,6 +1,7 @@
1
1
  import * as CM from 'complex-matcher';
2
2
  import { createLogger } from '@xen-orchestra/log';
3
3
  import { isPromise } from 'node:util/types';
4
+ import { ApiError } from './error.helper.mjs';
4
5
  export const NDJSON_CONTENT_TYPE = 'application/x-ndjson';
5
6
  const log = createLogger('xo:rest-api:utils-helper');
6
7
  export const isSrWritable = (sr) => isSrWritableOrIso(sr) && sr.content_type !== 'iso';
@@ -76,9 +77,22 @@ export function escapeUnsafeComplexMatcher(maybeString) {
76
77
  }
77
78
  return `(${maybeString})`;
78
79
  }
80
+ export function safeParseComplexMatcher(string) {
81
+ try {
82
+ return CM.parse(string);
83
+ }
84
+ catch (_error) {
85
+ // CM.parse only throw errors that are instances of Error
86
+ const error = _error;
87
+ const apiError = new ApiError(error.message, 400, { data: { stringToParse: string } });
88
+ apiError.cause = error;
89
+ apiError.stack = error.stack;
90
+ throw apiError;
91
+ }
92
+ }
79
93
  export function limitAndFilterArray(array, { filter, limit = Infinity } = {}) {
80
94
  if (filter !== undefined) {
81
- const predicate = typeof filter === 'string' ? CM.parse(filter).createPredicate() : filter;
95
+ const predicate = typeof filter === 'string' ? safeParseComplexMatcher(filter).createPredicate() : filter;
82
96
  array = array.filter(predicate);
83
97
  }
84
98
  if (limit < array.length) {
@@ -7,7 +7,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import * as CM from 'complex-matcher';
11
10
  import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
12
11
  import { inject } from 'inversify';
13
12
  import { noSuchObject } from 'xo-common/api-errors.js';
@@ -17,6 +16,7 @@ import { message, messageIds, partialMessages } from '../open-api/oa-examples/me
17
16
  import { RestApi } from '../rest-api/rest-api.mjs';
18
17
  import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
19
18
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
19
+ import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
20
20
  let MessageController = class MessageController extends XapiXoController {
21
21
  constructor(restapi) {
22
22
  super('message', restapi);
@@ -27,7 +27,7 @@ let MessageController = class MessageController extends XapiXoController {
27
27
  getObjects({ filter, limit = Infinity } = {}) {
28
28
  let userfilter = () => true;
29
29
  if (filter !== undefined) {
30
- userfilter = CM.parse(filter).createPredicate();
30
+ userfilter = safeParseComplexMatcher(filter).createPredicate();
31
31
  }
32
32
  const messagePredicate = (obj) => !alarmPredicate(obj) && userfilter(obj);
33
33
  return super.getObjects({ filter: messagePredicate, limit });
@@ -1,6 +1,6 @@
1
- import * as CM from 'complex-matcher';
2
1
  import { createLogger } from '@xen-orchestra/log';
3
2
  import { invalidCredentials } from 'xo-common/api-errors.js';
3
+ import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
4
4
  const log = createLogger('xo:rest-api:error-handler');
5
5
  export class RestApi {
6
6
  #ioc;
@@ -34,7 +34,7 @@ export class RestApi {
34
34
  }
35
35
  getObjectsByType(type, { filter, ...opts } = {}) {
36
36
  if (filter !== undefined && typeof filter === 'string') {
37
- filter = CM.parse(filter).createPredicate();
37
+ filter = safeParseComplexMatcher(filter).createPredicate();
38
38
  }
39
39
  return this.#xoApp.getObjectsByType(type, { filter, ...opts });
40
40
  }
@@ -7,7 +7,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import * as CM from 'complex-matcher';
11
10
  import { XoController } from '../abstract-classes/xo-controller.mjs';
12
11
  import { Get, Path, Query, Request, Route, Security, Tags, Response, Example, Delete, Post, SuccessResponse, } from 'tsoa';
13
12
  import { asynchronousActionResp, badRequestResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
@@ -17,6 +16,7 @@ import pDefer from 'promise-toolbox/defer';
17
16
  import { ApiError } from '../helpers/error.helper.mjs';
18
17
  import { Transform } from 'node:stream';
19
18
  import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs';
19
+ import { safeParseComplexMatcher } from '../helpers/utils.helper.mjs';
20
20
  let TaskController = class TaskController extends XoController {
21
21
  async getAllCollectionObjects() {
22
22
  const result = [];
@@ -41,7 +41,7 @@ let TaskController = class TaskController extends XoController {
41
41
  if (!ndjson) {
42
42
  throw new ApiError('watch=true requires ndjson=true', 400);
43
43
  }
44
- const userFilter = filter === undefined ? undefined : CM.parse(filter).createPredicate();
44
+ const userFilter = filter === undefined ? undefined : safeParseComplexMatcher(filter).createPredicate();
45
45
  const stream = new Transform({
46
46
  objectMode: true,
47
47
  transform([event, object], encoding, callback) {
@@ -12004,7 +12004,7 @@
12004
12004
  },
12005
12005
  "info": {
12006
12006
  "title": "@xen-orchestra/rest-api",
12007
- "version": "0.21.0",
12007
+ "version": "0.21.1",
12008
12008
  "description": "REST API to manage your XOA",
12009
12009
  "license": {
12010
12010
  "name": "AGPL-3.0-or-later"
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "main": "./dist/index.mjs",
7
7
  "name": "@xen-orchestra/rest-api",
8
8
  "homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/rest-api",
9
- "version": "0.21.0",
9
+ "version": "0.21.1",
10
10
  "description": "REST API to manage your XOA",
11
11
  "license": "AGPL-3.0-or-later",
12
12
  "private": false,