feat(api-errors): throw custom errors when XAPI error is caught (#440)

See vatesfr/xo-web#1717
This commit is contained in:
Pierre Donias 2016-11-07 14:15:23 +01:00 committed by Julien Fontanet
parent bc5b00781b
commit 2be7388696
24 changed files with 117 additions and 290 deletions

View File

@ -104,6 +104,7 @@
"xml2js": "~0.4.6",
"xo-acl-resolver": "^0.2.2",
"xo-collection": "^0.4.0",
"xo-common": "0.0.0",
"xo-remote-parser": "^0.3",
"xo-vmdk-to-vhd": "0.0.12"
},

View File

@ -1,70 +0,0 @@
import {JsonRpcError} from 'json-rpc-peer'
// ===================================================================
// Export standard JSON-RPC errors.
export { // eslint-disable-line no-duplicate-imports
InvalidJson,
InvalidParameters,
InvalidRequest,
JsonRpcError,
MethodNotFound
} from 'json-rpc-peer'
// -------------------------------------------------------------------
export class NotImplemented extends JsonRpcError {
constructor () {
super('not implemented', 0)
}
}
// -------------------------------------------------------------------
export class NoSuchObject extends JsonRpcError {
constructor (id, type) {
super('no such object', 1, {id, type})
}
}
// -------------------------------------------------------------------
export class Unauthorized extends JsonRpcError {
constructor () {
super('not authenticated or not enough permissions', 2)
}
}
// -------------------------------------------------------------------
export class InvalidCredential extends JsonRpcError {
constructor () {
super('invalid credential', 3)
}
}
// -------------------------------------------------------------------
export class AlreadyAuthenticated extends JsonRpcError {
constructor () {
super('already authenticated', 4)
}
}
// -------------------------------------------------------------------
export class ForbiddenOperation extends JsonRpcError {
constructor (operation, reason) {
super(`forbidden operation: ${operation}`, 5, reason)
}
}
// -------------------------------------------------------------------
// To be used with a user-readable message.
// The message can be destined to be displayed to the front-end user.
export class GenericError extends JsonRpcError {
constructor (message) {
super(message, 6)
}
}

View File

@ -1,4 +1,4 @@
import { Unauthorized } from '../api-errors'
import { unauthorized } from 'xo-common/api-errors'
export function create (props) {
return this.createIpPool(props)
@ -21,7 +21,7 @@ export function getAll (params) {
const { user } = this
if (!user) {
throw new Unauthorized()
throw unauthorized()
}
return this.getAllIpPools(user.permission === 'admin'

View File

@ -1,5 +1,3 @@
import {GenericError} from '../api-errors'
// ===================================================================
export async function set ({
@ -121,12 +119,7 @@ export {uploadPatch as patch}
// -------------------------------------------------------------------
export async function mergeInto ({ source, target, force }) {
try {
await this.mergeXenPools(source._xapiId, target._xapiId, force)
} catch (e) {
// FIXME: should we expose plain XAPI error messages?
throw new GenericError(e.message)
}
await this.mergeXenPools(source._xapiId, target._xapiId, force)
}
mergeInto.params = {

View File

@ -1,6 +1,6 @@
import {
Unauthorized
} from '../api-errors'
unauthorized
} from 'xo-common/api-errors'
// ===================================================================
@ -117,7 +117,7 @@ get.params = {
export async function getAll () {
const { user } = this
if (!user) {
throw new Unauthorized()
throw unauthorized()
}
return this.getAllResourceSets(user.id)

View File

@ -1,18 +1,14 @@
import {deprecate} from 'util'
import { getUserPublicProperties } from '../utils'
import {InvalidCredential, AlreadyAuthenticated} from '../api-errors'
import {invalidCredentials} from 'xo-common/api-errors'
// ===================================================================
export async function signIn (credentials) {
if (this.session.has('user_id')) {
throw new AlreadyAuthenticated()
}
const user = await this.authenticateUser(credentials)
if (!user) {
throw new InvalidCredential()
throw invalidCredentials()
}
this.session.set('user_id', user.id)

View File

@ -2,7 +2,7 @@ import forEach from 'lodash/forEach'
import getKeys from 'lodash/keys'
import moment from 'moment-timezone'
import { NoSuchObject } from '../api-errors'
import { noSuchObject } from 'xo-common/api-errors'
import { version as xoServerVersion } from '../../package.json'
// ===================================================================
@ -50,7 +50,7 @@ export function methodSignature ({method: name}) {
const method = this.apiMethods[name]
if (!method) {
throw new NoSuchObject()
throw noSuchObject()
}
// Return an array for compatibility with XML-RPC.

View File

@ -1,4 +1,4 @@
import {InvalidParameters} from '../api-errors'
import {invalidParameters} from 'xo-common/api-errors'
import { getUserPublicProperties, mapToArray } from '../utils'
// ===================================================================
@ -22,7 +22,7 @@ create.params = {
// Deletes an existing user.
async function delete_ ({id}) {
if (id === this.session.get('user_id')) {
throw new InvalidParameters('a user cannot delete itself')
throw invalidParameters('a user cannot delete itself')
}
await this.deleteUser(id)
@ -61,10 +61,10 @@ export async function set ({id, email, password, permission, preferences}) {
const isAdmin = this.user && this.user.permission === 'admin'
if (isAdmin) {
if (permission && id === this.session.get('user_id')) {
throw new InvalidParameters('a user cannot change its own permission')
throw invalidParameters('a user cannot change its own permission')
}
} else if (email || password || permission) {
throw new InvalidParameters('this properties can only changed by an administrator')
throw invalidParameters('this properties can only changed by an administrator')
}
await this.updateUser(id, {email, password, permission, preferences})

View File

@ -3,9 +3,9 @@
{coroutine: $coroutine} = require 'bluebird'
{format} = require 'json-rpc-peer'
{InvalidParameters} = require '../api-errors'
{invalidParameters} = require 'xo-common/api-errors'
{isArray: $isArray, parseSize} = require '../utils'
{JsonRpcError} = require '../api-errors'
{JsonRpcError} = require 'json-rpc-peer'
#=====================================================================
@ -38,7 +38,7 @@ set = $coroutine (params) ->
size = parseSize(params.size)
if size < vdi.size
throw new InvalidParameters(
throw invalidParameters(
"cannot set new size (#{size}) below the current size (#{vdi.size})"
)
yield xapi.resizeVdi(ref, size)

View File

@ -13,10 +13,10 @@ startsWith = require 'lodash/startsWith'
{format} = require 'json-rpc-peer'
{
GenericError,
InvalidParameters,
Unauthorized
} = require('../api-errors')
forbiddenOperation,
invalidParameters,
unauthorized
} = require('xo-common/api-errors')
{
forEach,
formatXml: $js2xml,
@ -49,7 +49,7 @@ checkPermissionOnSrs = (vm, permission = 'operate') -> (
)
return @hasPermissions(@session.get('user_id'), permissions).then((success) => (
throw new Unauthorized() unless success
throw unauthorized() unless success
))
)
@ -65,7 +65,7 @@ create = $coroutine (params) ->
{ user } = this
resourceSet = extract(params, 'resourceSet')
if not resourceSet and user.permission isnt 'admin'
throw new Unauthorized()
throw unauthorized()
template = extract(params, 'template')
params.template = template._xapiId
@ -402,7 +402,7 @@ migrate = $coroutine ({
])
unless yield @hasPermissions(@session.get('user_id'), permissions)
throw new Unauthorized()
throw unauthorized()
yield @getXapi(vm).migrateVm(vm._xapiId, @getXapi(host), host._xapiId, {
migrationNetworkId: migrationNetwork?._xapiId
@ -450,7 +450,7 @@ set = (params) ->
return @allocateLimitsInResourceSet(limits, resourceSet)
if (limits.cpuWeight && this.user.permission != 'admin')
throw new Unauthorized()
throw unauthorized()
)
set.params = {
@ -596,7 +596,7 @@ convertToTemplate = $coroutine ({vm}) ->
unless yield @hasPermissions(@session.get('user_id'), [
[ vm.$pool, 'administrate' ]
])
throw new Unauthorized()
throw unauthorized()
yield @getXapi(vm).call 'VM.set_is_a_template', vm._xapiRef, true
@ -791,10 +791,10 @@ exports.rollingBackup = rollingBackup
rollingDrCopy = ({vm, pool, sr, tag, depth}) ->
unless sr
unless pool
throw new InvalidParameters('either pool or sr param should be specified')
throw invalidParameters('either pool or sr param should be specified')
if vm.$pool is pool.id
throw new GenericError('Disaster Recovery attempts to copy on the same pool')
throw forbiddenOperation('Disaster Recovery attempts to copy on the same pool')
sr = @getObject(pool.default_SR, 'SR')
@ -852,7 +852,7 @@ stop = $coroutine ({vm, force}) ->
yield xapi.call 'VM.clean_shutdown', vm._xapiRef
catch error
if error.code is 'VM_MISSING_PV_DRIVERS' or error.code is 'VM_LACKS_FEATURE_SHUTDOWN'
throw new InvalidParameters('clean shutdown requires PV drivers')
throw invalidParameters('clean shutdown requires PV drivers')
else
throw error
@ -983,7 +983,7 @@ handleVmImport = $coroutine (req, res, { data, srId, type, xapi }) ->
res.end(format.response(0, vm.$id))
catch e
res.writeHead(500)
res.end(format.error(0, new GenericError(e.message)))
res.end(format.error(0, new Error(e.message)))
return
@ -991,12 +991,12 @@ handleVmImport = $coroutine (req, res, { data, srId, type, xapi }) ->
import_ = $coroutine ({ data, host, sr, type }) ->
if not sr
if not host
throw new InvalidParameters('you must provide either host or SR')
throw invalidParameters('you must provide either host or SR')
xapi = @getXapi(host)
sr = xapi.pool.$default_SR
if not sr
throw new InvalidParameters('there is not default SR in this pool')
throw invalidParameters('there is not default SR in this pool')
# FIXME: must have administrate permission on default SR.
else
@ -1203,7 +1203,7 @@ setBootOrder = $coroutine ({vm, order}) ->
yield xapi.call 'VM.set_HVM_boot_params', vm._xapiRef, order
return true
throw new InvalidParameters('You can only set the boot order on a HVM guest')
throw invalidParameters('You can only set the boot order on a HVM guest')
setBootOrder.params = {
vm: { type: 'string' },

View File

@ -18,7 +18,7 @@ import { createServer as createProxyServer } from 'http-proxy'
import { join as joinPath } from 'path'
import JsonRpcPeer from 'json-rpc-peer'
import { InvalidCredential } from './api-errors'
import { invalidCredentials } from 'xo-common/api-errors'
import {
readFile,
readdir
@ -475,7 +475,7 @@ const setUpConsoleProxy = (webServer, xo) => {
const user = await xo.authenticateUser({ token })
if (!await xo.hasPermissions(user.id, [ [ id, 'operate' ] ])) {
throw new InvalidCredential()
throw invalidCredentials()
}
const { remoteAddress } = socket

View File

@ -40,9 +40,7 @@ import {
promisifyAll,
pSettle
} from '../utils'
import {
ForbiddenOperation
} from '../api-errors'
import { forbiddenOperation } from 'xo-common/api-errors'
import mixins from './mixins'
import OTHER_CONFIG_TEMPLATE from './other-config-template'
@ -1346,7 +1344,7 @@ export default class Xapi extends XapiBase {
await this._startVm(this.getObject(vmId))
} catch (e) {
if (e.code === 'OPERATION_BLOCKED') {
throw new ForbiddenOperation('Start', e.params[1])
throw forbiddenOperation('Start', e.params[1])
}
throw e

View File

@ -6,7 +6,6 @@ import unzip from 'julien-f-unzip'
import httpProxy from '../../http-proxy'
import httpRequest from '../../http-request'
import { debounce } from '../../decorators'
import { GenericError } from '../../api-errors'
import {
createRawObject,
ensureArray,
@ -30,7 +29,7 @@ export default {
)
if (statusCode !== 200) {
throw new GenericError('cannot fetch patches list from Citrix')
throw new Error('cannot fetch patches list from Citrix')
}
const data = parseXml(await readAll()).patchdata

View File

@ -7,10 +7,8 @@ import schemaInspector from 'schema-inspector'
import * as methods from '../api'
import {
MethodNotFound,
InvalidParameters,
Unauthorized
} from '../api-errors'
MethodNotFound
} from 'json-rpc-peer'
import {
createRawObject,
forEach,
@ -19,6 +17,8 @@ import {
serializeError
} from '../utils'
import * as errors from 'xo-common/api-errors'
// ===================================================================
const PERMISSIONS = {
@ -28,6 +28,12 @@ const PERMISSIONS = {
admin: 3
}
const XAPI_ERROR_TO_XO_ERROR = {
NO_HOSTS_AVAILABLE: errors.noHostsAvailable,
SESSION_AUTHENTICATION_FAILED: errors.authenticationFailed,
EHOSTUNREACH: errors.hostUnreached
}
const hasPermission = (user, permission) => (
PERMISSIONS[user.permission] >= PERMISSIONS[permission]
)
@ -44,7 +50,7 @@ function checkParams (method, params) {
}, params)
if (!result.valid) {
throw new InvalidParameters(result.error)
throw errors.invalidParameters(result.error)
}
}
@ -60,7 +66,7 @@ function checkPermission (method) {
const {user} = this
if (!user) {
throw new Unauthorized()
throw errors.unauthorized()
}
// The only requirement is login.
@ -69,7 +75,7 @@ function checkPermission (method) {
}
if (!hasPermission(user, permission)) {
throw new Unauthorized()
throw errors.unauthorized()
}
}
@ -81,7 +87,7 @@ function resolveParams (method, params) {
const {user} = this
if (!user) {
throw new Unauthorized()
throw errors.unauthorized()
}
const userId = user.id
@ -117,7 +123,7 @@ function resolveParams (method, params) {
return params
}
throw new Unauthorized()
throw errors.unauthorized()
})
}
@ -282,6 +288,11 @@ export default class Api {
)
}
const xoError = XAPI_ERROR_TO_XO_ERROR[error.code]
if (xoError) {
throw xoError.error(error.params)
}
throw error
}
}

View File

@ -1,7 +1,5 @@
import Token, { Tokens } from '../models/token'
import {
NoSuchObject
} from '../api-errors'
import { noSuchObject } from 'xo-common/api-errors'
import {
createRawObject,
forEach,
@ -12,13 +10,8 @@ import {
// ===================================================================
class NoSuchAuthenticationToken extends NoSuchObject {
constructor (id) {
super(id, 'authentication token')
}
}
// ===================================================================
const noSuchAuthenticationToken = id =>
noSuchObject(id, 'authenticationToken')
export default class {
constructor (xo) {
@ -172,14 +165,14 @@ export default class {
async deleteAuthenticationToken (id) {
if (!await this._tokens.remove(id)) {
throw new NoSuchAuthenticationToken(id)
throw noSuchAuthenticationToken(id)
}
}
async getAuthenticationToken (id) {
let token = await this._tokens.first(id)
if (!token) {
throw new NoSuchAuthenticationToken(id)
throw noSuchAuthenticationToken(id)
}
token = token.properties
@ -189,7 +182,7 @@ export default class {
)) {
this._tokens.remove(id)::pCatch(noop)
throw new NoSuchAuthenticationToken(id)
throw noSuchAuthenticationToken(id)
}
return token

View File

@ -8,9 +8,9 @@ import keys from 'lodash/keys'
import mapValues from 'lodash/mapValues'
import pick from 'lodash/pick'
import remove from 'lodash/remove'
import { noSuchObject } from 'xo-common/api-errors'
import { fromCallback } from 'promise-toolbox'
import { NoSuchObject } from '../api-errors'
import {
forEach,
generateUnsecureToken,
@ -23,12 +23,6 @@ import {
// ===================================================================
class NoSuchIpPool extends NoSuchObject {
constructor (id) {
super(id, 'ip pool')
}
}
const normalize = ({
addresses,
id = throwFn('id is a required field'),
@ -90,7 +84,7 @@ export default class IpPools {
return store.del(id)
}
throw new NoSuchIpPool(id)
throw noSuchObject(id, 'ipPool')
}
async getAllIpPools (userId = undefined) {
@ -112,7 +106,7 @@ export default class IpPools {
getIpPool (id) {
return this._store.get(id).then(normalize, error => {
throw error.notFound ? new NoSuchIpPool(id) : error
throw error.notFound ? noSuchObject(id, 'ipPool') : error
})
}

View File

@ -3,18 +3,7 @@ import assign from 'lodash/assign'
import JobExecutor from '../job-executor'
import { Jobs } from '../models/job'
import { mapToArray } from '../utils'
import {
GenericError,
NoSuchObject
} from '../api-errors'
// ===================================================================
class NoSuchJob extends NoSuchObject {
constructor (id) {
super(id, 'job')
}
}
import { noSuchObject } from 'xo-common/api-errors'
// ===================================================================
@ -44,7 +33,7 @@ export default class {
async getJob (id) {
const job = await this._jobs.first(id)
if (!job) {
throw new NoSuchJob(id)
throw noSuchObject(id, 'job')
}
return job.properties
@ -67,24 +56,10 @@ export default class {
}
async runJobSequence (idSequence) {
const notFound = []
for (const id of idSequence) {
let job
try {
job = await this.getJob(id)
} catch (error) {
if (error instanceof NoSuchJob) {
notFound.push(id)
} else {
throw error
}
}
if (job) {
await this._executor.exec(job)
}
}
if (notFound.length > 0) {
throw new GenericError(`The following jobs were not found: ${notFound.join()}`)
const jobs = await Promise.all(mapToArray(idSequence, id => this.getJob(id)))
for (const job of jobs) {
await this._executor.exec(job)
}
}
}

View File

@ -2,9 +2,9 @@ import createJsonSchemaValidator from 'is-my-json-valid'
import { PluginsMetadata } from '../models/plugin-metadata'
import {
InvalidParameters,
NoSuchObject
} from '../api-errors'
invalidParameters,
noSuchObject
} from 'xo-common/api-errors'
import {
createRawObject,
isFunction,
@ -13,14 +13,6 @@ import {
// ===================================================================
class NoSuchPlugin extends NoSuchObject {
constructor (id) {
super(id, 'plugin')
}
}
// ===================================================================
export default class {
constructor (xo) {
this._plugins = createRawObject()
@ -43,7 +35,7 @@ export default class {
_getRawPlugin (id) {
const plugin = this._plugins[id]
if (!plugin) {
throw new NoSuchPlugin(id)
throw noSuchObject(id, 'plugin')
}
return plugin
}
@ -148,12 +140,12 @@ export default class {
const { configurationSchema } = plugin
if (!configurationSchema) {
throw new InvalidParameters('plugin not configurable')
throw invalidParameters('plugin not configurable')
}
// See: https://github.com/mafintosh/is-my-json-valid/issues/116
if (configuration == null) {
throw new InvalidParameters([{
throw invalidParameters([{
field: 'data',
message: 'is the wrong type'
}])
@ -161,7 +153,7 @@ export default class {
const validate = createJsonSchemaValidator(configurationSchema)
if (!validate(configuration)) {
throw new InvalidParameters(validate.errors)
throw invalidParameters(validate.errors)
}
// Sets the plugin configuration.
@ -200,11 +192,11 @@ export default class {
async loadPlugin (id) {
const plugin = this._getRawPlugin(id)
if (plugin.loaded) {
throw new InvalidParameters('plugin already loaded')
throw invalidParameters('plugin already loaded')
}
if (!plugin.configured) {
throw new InvalidParameters('plugin not configured')
throw invalidParameters('plugin not configured')
}
await plugin.instance.load()
@ -214,11 +206,11 @@ export default class {
async unloadPlugin (id) {
const plugin = this._getRawPlugin(id)
if (!plugin.loaded) {
throw new InvalidParameters('plugin already unloaded')
throw invalidParameters('plugin already unloaded')
}
if (plugin.unloadable === false) {
throw new InvalidParameters('plugin cannot be unloaded')
throw invalidParameters('plugin cannot be unloaded')
}
await plugin.instance.unload()

View File

@ -1,3 +1,5 @@
import { noSuchObject } from 'xo-common/api-errors'
import RemoteHandlerLocal from '../remote-handlers/local'
import RemoteHandlerNfs from '../remote-handlers/nfs'
import RemoteHandlerSmb from '../remote-handlers/smb'
@ -5,23 +7,12 @@ import {
forEach,
mapToArray
} from '../utils'
import {
NoSuchObject
} from '../api-errors'
import {
Remotes
} from '../models/remote'
// ===================================================================
class NoSuchRemote extends NoSuchObject {
constructor (id) {
super(id, 'remote')
}
}
// ===================================================================
export default class {
constructor (xo) {
this._remotes = new Remotes({
@ -74,7 +65,7 @@ export default class {
async _getRemote (id) {
const remote = await this._remotes.first(id)
if (!remote) {
throw new NoSuchRemote(id)
throw noSuchObject(id, 'remote')
}
return remote

View File

@ -2,11 +2,11 @@ import every from 'lodash/every'
import keyBy from 'lodash/keyBy'
import remove from 'lodash/remove'
import some from 'lodash/some'
import {
NoSuchObject,
Unauthorized
} from '../api-errors'
noSuchObject,
unauthorized
} from 'xo-common/api-errors'
import {
forEach,
generateUnsecureToken,
@ -19,12 +19,6 @@ import {
// ===================================================================
class NoSuchResourceSet extends NoSuchObject {
constructor (id) {
super(id, 'resource set')
}
}
const VM_RESOURCES = {
cpus: true,
disk: true,
@ -124,7 +118,7 @@ export default class {
// The set does not contains ALL objects.
!every(objectIds, lightSet(set.objects).has)
)) {
throw new Unauthorized()
throw unauthorized()
}
}
@ -156,7 +150,7 @@ export default class {
return store.del(id)
}
throw new NoSuchResourceSet(id)
throw noSuchObject(id, 'resourceSet')
}
async updateResourceSet (id, {
@ -223,7 +217,7 @@ export default class {
getResourceSet (id) {
return this._store.get(id).then(normalize, error => {
if (error.notFound) {
throw new NoSuchResourceSet(id)
throw noSuchObject(id, 'resourceSet')
}
throw error

View File

@ -1,7 +1,7 @@
import { BaseError } from 'make-error'
import { NoSuchObject } from '../api-errors.js'
import { Schedules } from '../models/schedule'
import { noSuchObject } from 'xo-common/api-errors.js'
import { Schedules } from '../models/schedule'
import {
forEach,
mapToArray,
@ -20,12 +20,6 @@ export class ScheduleOverride extends SchedulerError {
}
}
export class NoSuchSchedule extends NoSuchObject {
constructor (scheduleOrId) {
super(scheduleOrId, 'schedule')
}
}
export class ScheduleNotEnabled extends SchedulerError {
constructor (scheduleOrId) {
super('Schedule ' + _resolveId(scheduleOrId)) + ' is not enabled'
@ -96,7 +90,7 @@ export default class {
_disable (scheduleOrId) {
if (!this._exists(scheduleOrId)) {
throw new NoSuchSchedule(scheduleOrId)
throw noSuchObject(scheduleOrId, 'schedule')
}
if (!this._isEnabled(scheduleOrId)) {
throw new ScheduleNotEnabled(scheduleOrId)
@ -135,7 +129,7 @@ export default class {
const schedule = await this._redisSchedules.first(id)
if (!schedule) {
throw new NoSuchSchedule(id)
throw noSuchObject(id, 'schedule')
}
return schedule
@ -176,7 +170,7 @@ export default class {
const { properties } = schedule
if (!this._exists(properties)) {
throw new NoSuchSchedule(properties)
throw noSuchObject(properties, 'schedule')
}
if (this._isEnabled(properties)) {

View File

@ -5,11 +5,11 @@ import {
needsRehash,
verify
} from 'hashy'
import {
InvalidCredential,
NoSuchObject
} from '../api-errors'
invalidCredentials,
noSuchObject
} from 'xo-common/api-errors'
import {
Groups
} from '../models/group'
@ -27,18 +27,6 @@ import {
// ===================================================================
class NoSuchGroup extends NoSuchObject {
constructor (id) {
super(id, 'group')
}
}
class NoSuchUser extends NoSuchObject {
constructor (id) {
super(id, 'user')
}
}
const addToArraySet = (set, value) => set && !includes(set, value)
? set.concat(value)
: [ value ]
@ -181,7 +169,7 @@ export default class {
async _getUser (id) {
const user = await this._users.first(id)
if (!user) {
throw new NoSuchUser(id)
throw noSuchObject(id, 'user')
}
return user
@ -214,7 +202,7 @@ export default class {
return null
}
throw new NoSuchUser(username)
throw noSuchObject(username, 'user')
}
// Get or create a user associated with an auth provider.
@ -240,7 +228,7 @@ export default class {
async changeUserPassword (userId, oldPassword, newPassword) {
if (!(await this.checkUserPassword(userId, oldPassword, false))) {
throw new InvalidCredential()
throw invalidCredentials()
}
await this.updateUser(userId, { password: newPassword })
@ -303,7 +291,7 @@ export default class {
async getGroup (id) {
const group = await this._groups.first(id)
if (!group) {
throw new NoSuchGroup(id)
throw noSuchObject(id, 'group')
}
return group.properties

View File

@ -1,10 +1,8 @@
import { noSuchObject } from 'xo-common/api-errors'
import Xapi from '../xapi'
import xapiObjectToXo from '../xapi-object-to-xo'
import XapiStats from '../xapi-stats'
import {
GenericError,
NoSuchObject
} from '../api-errors'
import {
camelToSnakeCase,
createRawObject,
@ -21,14 +19,6 @@ import {
// ===================================================================
class NoSuchXenServer extends NoSuchObject {
constructor (id) {
super(id, 'xen server')
}
}
// ===================================================================
export default class {
constructor (xo) {
this._objectConflicts = createRawObject() // TODO: clean when a server is disconnected.
@ -84,7 +74,7 @@ export default class {
this.disconnectXenServer(id)::pCatch(noop)
if (!await this._servers.remove(id)) {
throw new NoSuchXenServer(id)
throw noSuchObject(id, 'xenServer')
}
}
@ -115,7 +105,7 @@ export default class {
async _getXenServer (id) {
const server = await this._servers.first(id)
if (!server) {
throw new NoSuchXenServer(id)
throw noSuchObject(id, 'xenServer')
}
return server
@ -288,23 +278,13 @@ export default class {
xapi.xo.install()
try {
await xapi.connect()
} catch (error) {
if (error.code === 'SESSION_AUTHENTICATION_FAILED') {
throw new GenericError('authentication failed')
}
if (error.code === 'EHOSTUNREACH') {
throw new GenericError('host unreachable')
}
throw error
}
await xapi.connect()
}
async disconnectXenServer (id) {
const xapi = this._xapis[id]
if (!xapi) {
throw new NoSuchXenServer(id)
throw noSuchObject(id, 'xenServer')
}
delete this._xapis[id]

View File

@ -3,6 +3,7 @@ import XoCollection from 'xo-collection'
import XoUniqueIndex from 'xo-collection/unique-index'
import {createClient as createRedisClient} from 'redis'
import {EventEmitter} from 'events'
import { noSuchObject } from 'xo-common/api-errors'
import mixins from './xo-mixins'
import Connection from './connection'
@ -20,9 +21,6 @@ import {
mapToArray,
noop
} from './utils'
import {
NoSuchObject
} from './api-errors'
// ===================================================================
@ -140,14 +138,14 @@ export default class Xo extends EventEmitter {
const obj = all[key] || byRef[key]
if (!obj) {
throw new NoSuchObject(key, type)
throw noSuchObject(key, type)
}
if (type != null && (
isString(type) && type !== obj.type ||
!includes(type, obj.type) // Array
)) {
throw new NoSuchObject(key, type)
throw noSuchObject(key, type)
}
return obj