UNPKG

@adonisjs/route-model-binding

Version:
224 lines (218 loc) 6.59 kB
import { __name, resolveRouteHandler } from "./chunk-FAJSNHSW.js"; // src/resource_loader.ts import string from "@poppinss/utils/string"; // src/params_parser.ts var ParamsParser = class { static { __name(this, "ParamsParser"); } #params; #routePattern; constructor(params, routePattern) { this.#params = params; this.#routePattern = routePattern; } /** * A param can be one of the following * * post * post(slug) * >comment * >comment(slug) */ #parseParam(param) { let scoped = false; let [name, lookupKey] = param.split("("); if (lookupKey) { lookupKey = lookupKey.slice(0, -1); } else { lookupKey = "$primaryKey"; } if (name.startsWith(">")) { scoped = true; name = name.substring(1); } return { scoped, name, param, lookupKey, parent: null }; } /** * Loop through the params and setup the parents */ #computeParamParents(params) { params.forEach((param, index) => { if (param.scoped) { if (index === 0) { throw new Error(`The first parameter in route "${this.#routePattern}" cannot be scoped`); } param.parent = params[index - 1].name; } }); return params; } /** * Parse route params for a given request */ parse() { return this.#computeParamParents(this.#params.map((param) => this.#parseParam(param))); } }; // src/exceptions/missing_relationship.ts import { Exception } from "@poppinss/utils"; var MissingRelationshipException = class extends Exception { static { __name(this, "MissingRelationshipException"); } static invoke(paramName, route, parentModel) { const errorMessage = `Cannot load "${paramName}" for route "${route}". Make sure to define it as a relationship on model "${parentModel}"`; return new this(errorMessage, { code: "E_MISSING_RELATIONSHIP", status: 500 }); } }; // src/resource_loader.ts var ResourceLoader = class { static { __name(this, "ResourceLoader"); } ctx; #router; resources; constructor(ctx, router) { this.ctx = ctx; this.resources = {}; this.#router = router; } /** * Returns true when value is a resource model */ #isResourceModel(value) { if (!value) { return false; } return typeof value["findForRequest"] === "function" || typeof value["findOrFail"] === "function" || typeof value["findRelatedForRequest"] === "function"; } /** * Returns the relationship name for a scoped resource */ #getRelationshipName(param, parentModel) { let relationshipName = string.singular(string.camelCase(param.name)); if (parentModel.$hasRelation(relationshipName)) { return relationshipName; } relationshipName = string.pluralize(relationshipName); if (parentModel.$hasRelation(relationshipName)) { return relationshipName; } throw MissingRelationshipException.invoke(param.name, this.ctx.route.pattern, parentModel.name); } /** * Instantiate scoped model. The parent model instance is passed as * the first argument. */ async #instantiateScopedModel(model, param, value) { const parentModel = this.resources[param.parent]; const parentModelConstructor = parentModel.constructor; if (typeof parentModel["findRelatedForRequest"] === "function") { return parentModel["findRelatedForRequest"](this.ctx, param, value); } const relatedQuery = parentModel.related(this.#getRelationshipName(param, parentModelConstructor)).query(); if (param.lookupKey !== "$primaryKey") { return relatedQuery.where(param.lookupKey, value).firstOrFail(); } if (model.routeLookupKey) { return relatedQuery.where(model.routeLookupKey, value).firstOrFail(); } return relatedQuery.where(model.primaryKey, value).firstOrFail(); } /** * Instantiate model */ async #instantiateModel(model, param, value) { if (typeof model.findForRequest === "function") { return model.findForRequest(this.ctx, param, value); } if (param.lookupKey !== "$primaryKey") { return model.findByOrFail(param.lookupKey, value); } if (model.routeLookupKey) { return model.findByOrFail(model.routeLookupKey, value); } return model.findOrFail(value); } /** * Rewrite ctx.params to use normalized param names */ #rewriteParams(params) { params.forEach((param) => { if (param.name !== param.param) { this.ctx.params[param.name] = this.ctx.params[param.param]; delete this.ctx.params[param.param]; } }); } /** * Load models based upon the current request route params */ async load(models) { let index = 0; if (!this.ctx.route.meta.resolvedParams) { this.ctx.route.meta.resolvedParams = new ParamsParser(this.#router.match(this.ctx.request.url(), this.ctx.request.method()).route.meta.params, this.ctx.route.pattern).parse(); } const routeParams = this.ctx.route.meta.resolvedParams; for (let param of routeParams) { const model = models[index]; const value = this.ctx.request.param(param.param); if (value !== void 0 && value !== null) { if (this.#isResourceModel(model)) { this.resources[param.name] = param.scoped ? await this.#instantiateScopedModel(model, param, value) : await this.#instantiateModel(model, param, value); } else { this.resources[param.name] = value; } } index++; } this.#rewriteParams(routeParams); } }; // src/rmb_middleware.ts var RouteModelBindingMiddleware = class { static { __name(this, "RouteModelBindingMiddleware"); } #app; constructor(application) { this.#app = application; } async handle(ctx, next) { ctx.resources = {}; const route = ctx.route; if (!route) { return next(); } const handler = route.handler; if (!handler || typeof handler === "function") { return next(); } const { controllerConstructor, method } = await resolveRouteHandler(handler.reference, this.#app); if (!controllerConstructor || !controllerConstructor.bindings || controllerConstructor.bindings && !controllerConstructor.bindings[method]) { return next(); } const resourceLoader = new ResourceLoader(ctx, await this.#app.container.make("router")); await resourceLoader.load(controllerConstructor["bindings"][method]); ctx.resources = resourceLoader.resources; await next(); } }; export { RouteModelBindingMiddleware }; //# sourceMappingURL=chunk-LNBQB6LP.js.map