@avonjs/avonjs
Version:
A fluent Node.js API generator.
236 lines (235 loc) • 7.99 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const change_case_all_1 = require("change-case-all");
const collect_js_1 = __importDefault(require("collect.js"));
const Avon_1 = __importDefault(require("../Avon"));
const FieldCollection_1 = __importDefault(require("../Collections/FieldCollection"));
const Contracts_1 = require("../Contracts");
const Exceptions_1 = require("../Exceptions");
const Filters_1 = require("../Filters");
const Orderings_1 = require("../Orderings");
const Lazy_1 = __importDefault(require("./Lazy"));
const ResourceRelationshipGuesser_1 = require("./ResourceRelationshipGuesser");
class Relation extends Lazy_1.default {
/**
* Name of the relationship.
*/
relation;
/**
* The related resource instance
*/
relatedResource;
/**
* The foreign key of the parent model.
* The attribute name that holds the parent model key.
*/
foreignKey;
/**
* The associated key on the child model.
* Defaults to primary key of parent model.
*/
ownerKey;
/**
* The callback that should be run to associate relations.
*/
relatableQueryCallback = (request, repository) => this.relatedResource.relatableQuery(request, repository) ?? repository;
constructor(resource, relation) {
const relatedResource = Avon_1.default.resourceForKey(resource);
Exceptions_1.RuntimeException.when(relatedResource === undefined, `Resource '${resource}' not found for relationship ${relation ?? resource}`);
super(relation ?? (0, ResourceRelationshipGuesser_1.guessRelation)(relatedResource));
this.relation = this.attribute;
this.relatedResource = relatedResource;
this.ownerKey = this.relatedResource.repository().model().getKeyName();
this.foreignKey = (0, change_case_all_1.snakeCase)(`${this.relation}_${this.ownerKey}`);
}
/**
* Indicates fields uses to display in relation request.
*/
relatableFields = (request) => {
const fields = this.relatedResource.fieldsForAssociation(request);
return new FieldCollection_1.default(fields)
.filterForAssociation(request)
.withoutUnresolvableFields()
.withoutRelatableFields()
.all();
};
/**
* Get all of the possibly available filters for the request.
*/
availableFilters(request) {
return new FieldCollection_1.default(this.relatableFields(request))
.withOnlyFilterableFields()
.authorized(request)
.map((field) => field.resolveFilter(request))
.filter((filter) => filter instanceof Filters_1.Filter)
.merge(this.relatedResourceFilters(request))
.unique((filter) => filter.key())
.all();
}
/**
* Resolve the related resource filters.
*/
relatedResourceFilters(request) {
return this.relatedResource
.resolveFilters(request)
.filter((filter) => filter.authorizedToSee(request));
}
/**
* Get all of the possibly available filters for the request.
*/
availableOrderings(request) {
return new FieldCollection_1.default(this.relatableFields(request))
.withOnlyOrderableFields()
.authorized(request)
.map((field) => field.resolveOrdering(request))
.filter((ordering) => ordering instanceof Orderings_1.Ordering)
.merge(this.relatedResourceOrderings(request))
.unique((ordering) => ordering.key())
.all();
}
/**
* Resolve the related resource orderings.
*/
relatedResourceOrderings(request) {
return this.relatedResource
.resolveOrderings(request)
.filter((ordering) => ordering.authorizedToSee(request));
}
/**
* Mutate the field value for response.
*/
getMutatedValue(request, value) {
return (0, collect_js_1.default)(value)
.map((relatable) => this.formatRelatedResource(request, relatable))
.values()
.all();
}
/**
* Format the given related resource.
*/
formatRelatedResource(request, resource) {
return new FieldCollection_1.default(this.relatableFields(request))
.resolve(resource)
.fieldValues(request);
}
/**
* Set related model foreign key.
*/
withForeignKey(foreignKey) {
this.foreignKey = foreignKey;
return this;
}
/**
* Get attribute that hold the related model key.
*/
foreignKeyName(request) {
return this.foreignKey;
}
/**
* Set related model owner key.
*/
withOwnerKey(ownerKey) {
this.ownerKey = ownerKey;
return this;
}
/**
* Get attribute that hold the related model key.
*/
ownerKeyName(request) {
return this.ownerKey;
}
/**
* Determine display fields.
*/
fields(callback) {
this.relatableFields = callback;
return this;
}
/**
* Resolve value for given resources.
*/
async resolveForResources(request, resources) {
return this.resolveRelatables(request, resources);
}
/**
* Resolve related value for given resources.
*/
async resolveRelatables(request, resources) {
const relatables = await this.searchRelatables(request, resources);
resources.forEach((resource) => {
resource.setAttribute(this.attribute, relatables.filter((relatable) => {
return (
// biome-ignore lint/suspicious/noDoubleEquals:
relatable.getAttribute(this.ownerKeyName(request)) ==
resource.getAttribute(this.foreignKeyName(request)));
}));
});
}
/**
* Search associable resources.
*/
async searchAssociable(request, withTrashed = false) {
const repository = await this.relatedResource.search(request, request.filters(), request.orderings(), withTrashed ? Contracts_1.TrashedStatus.WITH : Contracts_1.TrashedStatus.DEFAULT);
const query = this.relatableQueryCallback.apply(repository, [
request,
repository,
]);
return query ?? repository;
}
/**
* Determine the associate relations query.
*/
relatableQueryUsing(relatableQueryCallback) {
this.relatableQueryCallback = relatableQueryCallback;
return this;
}
/**
* Define filterable attribute.
*/
filterableAttribute(request) {
return this.foreignKeyName(request);
}
/**
* Resolve the field's value.
*/
resolve(resource, attribute) {
super.resolve(resource, this.isLoaded() || this.foreignKey === '' ? attribute : this.foreignKey);
}
/**
* Determine field is filterable or not.
*/
isFilterable() {
return true;
}
/**
* Determine field is orderable or not.
*/
isOrderable() {
return false;
}
responseSchema(request) {
const fields = this.schemaFields(request);
return {
...super.responseSchema(request),
type: 'array',
default: fields.fieldValues(request),
items: { type: 'object', properties: fields.responseSchemas(request) },
};
}
payloadSchema(request) {
const fields = this.schemaFields(request);
return {
...super.payloadSchema(request),
type: 'array',
default: fields.fieldValues(request),
items: { type: 'object', properties: fields.payloadSchemas(request) },
};
}
schemaFields(request) {
return new FieldCollection_1.default(this.relatableFields(request));
}
}
exports.default = Relation;