dashboard/plugins/steve/resource-proxy.js

98 lines
2.6 KiB
JavaScript

import { lookup } from './model-loader';
import ResourceInstance from './resource-instance';
export const SELF = '__[[SELF]]__';
export const ALREADY_A_PROXY = '__[[PROXY]]__';
export const PRIVATE = '__[[PRIVATE]]__';
const FAKE_CONSTRUCTOR = function() {};
FAKE_CONSTRUCTOR.toString = function() {
return 'ResourceProxy';
};
const nativeProperties = ['description'];
export function proxyFor(ctx, obj, isClone = false) {
// Attributes associated to the proxy, but not stored on the actual backing object
let priv;
if ( obj[ALREADY_A_PROXY] ) {
return obj;
}
if ( process.server ) {
Object.defineProperty(obj, '__rehydrate', {
value: ctx.state.config.namespace,
enumerable: true,
configurable: true
});
if ( isClone ) {
Object.defineProperty(obj, '__clone', {
value: true,
enumerable: true,
configurable: true,
writable: true
});
}
}
const mappedType = ctx.rootGetters['type-map/componentFor'](obj.type);
const customModel = lookup(mappedType, obj?.metadata?.name);
const model = customModel || ResourceInstance;
// Hack for now, the resource-instance name() overwrites the model name.
if ( obj.name ) {
obj._name = obj.name;
delete obj.name;
}
const proxy = new Proxy(obj, {
get(target, name) {
if ( name === ALREADY_A_PROXY ) {
return true;
} else if ( name === SELF ) {
return obj;
} else if ( name === Symbol.toStringTag ) {
name = 'toString';
} else if ( typeof name !== 'string' ) {
return target[name];
} else if ( name === 'constructor' ) {
// To satisfy vue-devtools
return FAKE_CONSTRUCTOR;
} else if ( name.startsWith('$') ) {
return ctx[name.substr(1)];
} else if ( name === PRIVATE ) {
if ( !priv ) {
priv = {};
}
return priv;
}
let fn;
if ( customModel && Object.prototype.hasOwnProperty.call(customModel, name) ) {
fn = model[name];
} else if (nativeProperties.includes(name) && obj[name] !== undefined) {
// If there's not a model specific override for this property check if it exists natively in the object... otherwise fall back on
// the default resource instance property/fn. This ensures it's correctly stored over fetch/clone/etc and sent when persisted
return obj[name];
}
if ( !fn ) {
fn = ResourceInstance[name];
}
if ( fn && fn.call ) {
return fn.call(proxy);
}
return target[name];
},
});
return proxy;
}