168 lines
4.7 KiB
JavaScript
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);
|
|
}
|
|
}
|