adminjs-graphql
Version:
adminjs GraphQL adapter
319 lines • 12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports._testing = exports.GraphQLResourceAdapter = void 0;
const graphql_1 = require("graphql");
const adminjs_1 = require("adminjs");
class GraphQLResourceAdapter extends adminjs_1.BaseResource {
constructor(rawResource) {
var _a, _b;
super(rawResource);
this.rawResource = rawResource;
if (!rawResource.connection) {
throw new Error("Uninitialized resource");
}
this.connection = rawResource.connection;
this.propertyMap = new Map((_b = (_a = rawResource.properties) === null || _a === void 0 ? void 0 : _a.map((prop) => [prop.path(), prop])) !== null && _b !== void 0 ? _b : []);
}
databaseName() {
return this.connection.name;
}
databaseType() {
return "graphql";
}
id() {
return this.rawResource.id;
}
properties() {
return [...this.propertyMap.values()];
}
property(path) {
var _a;
return (_a = this.propertyMap.get(path)) !== null && _a !== void 0 ? _a : null;
}
async count(filter) {
try {
const fieldFilter = this.mapFilter(filter);
const mapping = this.rawResource.count(fieldFilter);
return await this.executeMapping(mapping);
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
async find(filter, options) {
try {
const fieldFilter = this.mapFilter(filter);
const mapping = this.rawResource.find(fieldFilter, options);
const result = await this.executeMapping(mapping);
return result.map((record) => new adminjs_1.BaseRecord(record, this));
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
async findOne(id) {
try {
const mapping = this.rawResource.findOne(deflateReference(id));
const result = await this.executeMapping(mapping);
if (result) {
return new adminjs_1.BaseRecord(result, this);
}
return null;
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
async findMany(ids) {
const resolved = await Promise.all(ids.map((id) => this.findOne(id)));
return resolved.filter((record) => record != null);
}
convertParams(params) {
const converted = Object.keys(params).reduce((coerced, key) => {
var _a, _b, _c;
let value = params[key];
try {
if (value != null) {
const type = (_b = (_a = this.rawResource) === null || _a === void 0 ? void 0 : _a.typeMap) === null || _b === void 0 ? void 0 : _b.get(key);
if (type instanceof graphql_1.GraphQLScalarType) {
value = type.serialize(value);
}
}
coerced[key] = value;
return coerced;
}
catch (thrown) {
const error = thrown;
if (value === "" && !((_c = this.propertyMap.get(key)) === null || _c === void 0 ? void 0 : _c.isRequired())) {
coerced[key] = null;
return coerced;
}
throw new adminjs_1.ValidationError({
[key]: {
type: "conversion",
message: error.message,
},
});
}
}, {});
return converted;
}
validateParams(params) {
var _a, _b, _c, _d, _e, _f;
const errors = {};
const editProperties = (_c = (_b = (_a = this._decorated) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.editProperties) !== null && _c !== void 0 ? _c : [];
for (const key of this.properties().map((p) => p.path())) {
const property = (_d = this._decorated) === null || _d === void 0 ? void 0 : _d.getPropertyByKey(key);
const value = params[key];
// Skip properties that are not being edited
if (editProperties.length &&
!editProperties.includes((_e = property === null || property === void 0 ? void 0 : property.property.path()) !== null && _e !== void 0 ? _e : "")) {
continue;
}
// Skip self ID properties
if ((property === null || property === void 0 ? void 0 : property.isId()) && (property === null || property === void 0 ? void 0 : property.type()) !== "reference") {
continue;
}
const required = (_f = property === null || property === void 0 ? void 0 : property.options.isRequired) !== null && _f !== void 0 ? _f : property === null || property === void 0 ? void 0 : property.isRequired();
if (required) {
if (value === "" || value === null || value === undefined) {
errors[key] = {
type: "required",
message: "Required field",
};
}
}
}
if (Object.keys(errors).length) {
throw new adminjs_1.ValidationError(errors);
}
}
async create(params) {
var _a, _b;
try {
const inflated = inflateParams(this.convertParams(params));
this.validateParams(inflated);
const mapping = (_b = (_a = this.rawResource).create) === null || _b === void 0 ? void 0 : _b.call(_a, inflated);
if (!mapping) {
throw new adminjs_1.ForbiddenError("Resource is not editable");
}
return await this.executeMapping(mapping);
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
async update(id, params) {
var _a, _b;
try {
const inflated = inflateParams(this.convertParams(params));
this.validateParams(inflated);
const mapping = (_b = (_a = this.rawResource).update) === null || _b === void 0 ? void 0 : _b.call(_a, id, inflated);
if (!mapping) {
throw new adminjs_1.ForbiddenError("Resource is not editable");
}
return await this.executeMapping(mapping);
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
async delete(id) {
var _a, _b;
try {
const mapping = (_b = (_a = this.rawResource).delete) === null || _b === void 0 ? void 0 : _b.call(_a, id);
if (!mapping) {
throw new adminjs_1.ForbiddenError("Resource is not editable");
}
await this.executeMapping(mapping);
}
catch (error) {
this.connection.reportAndThrow(error);
}
}
static isAdapterFor(resource) {
const internalResource = resource;
return internalResource.tag == "GraphQLResource";
}
async executeMapping(mapping) {
var _a;
const queryString = typeof mapping.query === "string"
? mapping.query
: (_a = mapping.query.loc) === null || _a === void 0 ? void 0 : _a.source.body;
if (!queryString) {
this.connection.reportAndThrow(new Error("Unexpected parsed query without body"));
}
const result = await this.connection.request(queryString, mapping.variables);
const parsed = mapping.parseResult(result);
if (!this.rawResource.makeSubproperties) {
if (parsed instanceof Array) {
return parsed.map((p) => deflateParams(p));
}
else {
return deflateParams(parsed);
}
}
return parsed;
}
mapFilter(filter) {
return filter.reduce((mapped, element) => {
var _a;
const from = typeof element.value == "string"
? element.value
: element.value.from;
const to = typeof element.value == "string" ? from : element.value.to;
const matchOperation = element.property.type() === "string" ? "MATCH" : "EQ";
let graphQLType = (_a = this.rawResource.typeMap) === null || _a === void 0 ? void 0 : _a.get(element.path);
if (graphQLType instanceof graphql_1.GraphQLObjectType) {
graphQLType = graphql_1.GraphQLID;
}
if (!graphQLType || !(0, graphql_1.isInputType)(graphQLType)) {
this.connection.reportAndThrow(new Error(`Cannot get valid GraphQL type from ${this.rawResource.id}:${element.path}`));
}
const coercedFrom = convertValue(from, graphQLType);
const coercedTo = convertValue(to, graphQLType);
if (from === to) {
mapped.push({
field: element.property.path(),
is: matchOperation,
to: coercedFrom,
});
}
else {
if (from !== undefined && from != "") {
mapped.push({
field: element.property.path(),
is: "GTE",
to: coercedFrom,
});
}
if (to !== undefined && to != "") {
mapped.push({
field: element.property.path(),
is: "LTE",
to: coercedTo,
});
}
}
return mapped;
}, []);
}
}
exports.GraphQLResourceAdapter = GraphQLResourceAdapter;
function inflateParams(params) {
const record = {};
for (const path of Object.keys(params)) {
const steps = path.split(".");
let object = record;
let index = -1;
while (steps.length > 1) {
const step = steps.shift();
let nextObject = {};
if (step === undefined) {
break;
}
index = parseInt(steps[0]);
if (!isNaN(index)) {
nextObject = [];
}
object[step] = object[step] || nextObject;
object = object[step];
}
if (object instanceof Array) {
object.length = index + 1;
object[index] = params[path];
}
else {
object[steps[0]] = params[path];
}
}
return record;
}
function deflateParams(params, IDField = "ID") {
if (typeof params !== "object" || params == null) {
return params;
}
const typed = params;
const record = {};
for (const key of Object.keys(typed)) {
let param = typed[key];
if (typeof param === "object" && param !== null) {
const deflated = deflateParams(param);
const deflatedKeys = Object.keys(deflated);
if (deflatedKeys.length === 1 && IDField in deflated) {
// Reference hack!
param = Object.values(deflated)[0];
record[key] = param;
}
else {
for (const subKey of deflatedKeys) {
record[`${key}.${subKey}`] = deflated[subKey];
}
}
}
else {
record[key] = param;
}
}
return record;
}
function deflateReference(ref) {
if (typeof ref === "object" && ref !== null) {
const fields = Object.values(ref);
if (fields.length === 1) {
return fields[0];
}
}
return `${ref}`;
}
function convertValue(value, type) {
if (type instanceof graphql_1.GraphQLScalarType) {
return type.serialize(value);
}
return value;
}
exports._testing = {
inflateParams,
deflateParams,
};
//# sourceMappingURL=GraphQLResource.js.map