consul/ui/packages/consul-ui/app/adapters/http.js

168 lines
4.7 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import { inject as service } from '@ember/service';
import Adapter from '@ember-data/adapter';
import AdapterError, {
AbortError,
TimeoutError,
ServerError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
ConflictError,
InvalidError,
} from '@ember-data/adapter/error';
// TODO These are now exactly the same, apart from the fact that one uses
// `serialized, unserialized` and the other just `query`
// they could actually be one function now, but would be nice to think about
// the naming of things (serialized vs query etc)
const read = function (adapter, modelName, type, query = {}) {
return adapter.rpc(
function (adapter, ...rest) {
return adapter[`requestFor${type}`](...rest);
},
function (serializer, ...rest) {
return serializer[`respondFor${type}`](...rest);
},
query,
modelName
);
};
const write = function (adapter, modelName, type, snapshot) {
return adapter.rpc(
function (adapter, ...rest) {
return adapter[`requestFor${type}`](...rest);
},
function (serializer, ...rest) {
return serializer[`respondFor${type}`](...rest);
},
snapshot,
modelName
);
};
export default class HttpAdapter extends Adapter {
@service('client/http') client;
rpc(req, resp, obj, modelName) {
const client = this.client;
const store = this.store;
const adapter = this;
let unserialized, serialized;
const serializer = store.serializerFor(modelName);
const modelClass = store.modelFor(modelName);
// workable way to decide whether this is a snapshot
// essentially 'is attributable'.
// Snapshot is private so we can't do instanceof here
// and using obj.constructor.name gets changed/minified
// during compilation so you can't rely on it
// checking for `attributes` being a function is more
// reliable as that is the thing we need to call
if (typeof obj.attributes === 'function') {
unserialized = obj.attributes();
serialized = serializer.serialize(obj, {});
} else {
unserialized = obj;
serialized = unserialized;
}
return client
.request(function (request) {
return req(adapter, request, serialized, unserialized, modelClass);
})
.catch(function (e) {
return adapter.error(e);
})
.then(function (respond) {
// TODO: When HTTPAdapter:responder changes, this will also need to change
return resp(serializer, respond, serialized, unserialized, modelClass);
});
// TODO: Potentially add specific serializer errors here
// .catch(function(e) {
// return Promise.reject(e);
// });
}
error(err) {
if (err instanceof TypeError) {
throw err;
}
const errors = [
{
status: `${err.statusCode}`,
title: 'The backend responded with an error',
detail: err.message,
},
];
let error;
const detailedMessage = '';
try {
switch (err.statusCode) {
case 0:
error = new AbortError();
error.errors[0].status = '0';
break;
case 401:
error = new UnauthorizedError(errors, detailedMessage);
break;
case 403:
error = new ForbiddenError(errors, detailedMessage);
break;
case 404:
error = new NotFoundError(errors, detailedMessage);
break;
case 408:
error = new TimeoutError();
break;
case 409:
error = new ConflictError(errors, detailedMessage);
break;
case 422:
error = new InvalidError(errors); //payload.errors
break;
default:
if (err.statusCode >= 500) {
error = new ServerError(errors, detailedMessage);
} else {
error = new AdapterError(errors, detailedMessage);
}
}
} catch (e) {
error = e;
}
// TODO: This comes originates from ember-data
// This can be confusing if you need to use this with Promise.reject
// Consider changing this to return the error and then
// throw from the call site instead
throw error;
}
query(store, type, query) {
return read(this, type.modelName, 'Query', query);
}
queryRecord(store, type, query) {
return read(this, type.modelName, 'QueryRecord', query);
}
findAll(store, type) {
return read(this, type.modelName, 'FindAll');
}
createRecord(store, type, snapshot) {
return write(this, type.modelName, 'CreateRecord', snapshot);
}
updateRecord(store, type, snapshot) {
return write(this, type.modelName, 'UpdateRecord', snapshot);
}
deleteRecord(store, type, snapshot) {
return write(this, type.modelName, 'DeleteRecord', snapshot);
}
}