Skip to content

Commit

Permalink
Fixed #132
Browse files Browse the repository at this point in the history
  • Loading branch information
ozziest committed Oct 29, 2023
1 parent 237a293 commit 64bbc11
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 15 deletions.
21 changes: 19 additions & 2 deletions src/Builders/RouterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IModelService,
IRelation,
IRouteData,
IRouteParentPair,
IVersion,
} from "../Interfaces";
import { API_ROUTE_TEMPLATES, HANDLER_METHOD_MAP } from "../constants";
Expand Down Expand Up @@ -48,12 +49,13 @@ class RouterBuilder {

private async createRoutesByModelTree() {
for (const model of this.version.modelTree) {
await this.createRouteByModel(model);
await this.createRouteByModel(model, []);
}
}

private async createRouteByModel(
model: IModelService,
parentPairs: IRouteParentPair[] = [],
urlPrefix = "",
parentModel: IModelService | null = null,
relation: IRelation | null = null,
Expand Down Expand Up @@ -92,12 +94,13 @@ class RouterBuilder {
url,
middlewares,
model,
parentPairs,
parentModel,
relation,
);
}

await this.createChildRoutes(model, resource, urlPrefix);
await this.createChildRoutes(model, resource, urlPrefix, parentPairs);
await this.createNestedRoutes(model, allowRecursive, urlPrefix, resource);
}

Expand All @@ -120,8 +123,13 @@ class RouterBuilder {

if (relation) {
const paramName = camelCase(`${model.name}-${relation.primaryKey}`);
const parentPair: IRouteParentPair = {
model,
paramName,
};
await this.createRouteByModel(
model,
[parentPair],
`${urlPrefix}${resource}/:${paramName}/`,
model,
relation,
Expand All @@ -134,6 +142,7 @@ class RouterBuilder {
model: IModelService,
resource: string,
urlPrefix: string,
parentPairs: IRouteParentPair[],
) {
if (model.children.length === 0) {
return;
Expand All @@ -148,8 +157,14 @@ class RouterBuilder {
// It should be recursive
if (child) {
const paramName = camelCase(`${model.name}-${relation.primaryKey}`);
// Setting the new parent pair depth
const parentPair: IRouteParentPair = {
model,
paramName,
};
await this.createRouteByModel(
child,
[...parentPairs, parentPair],
`${urlPrefix}${resource}/:${paramName}/`,
model,
relation,
Expand All @@ -163,6 +178,7 @@ class RouterBuilder {
url: string,
middlewares: AxeFunction[],
model: IModelService,
parentPairs: IRouteParentPair[],
parentModel: IModelService | null,
relation: IRelation | null,
) {
Expand All @@ -182,6 +198,7 @@ class RouterBuilder {
url,
data,
middlewares,
parentPairs,
);

// Documentation
Expand Down
6 changes: 6 additions & 0 deletions src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ export interface IURLPair {
phases: IPhaseDefinition[];
hasTransaction: boolean;
params?: any;
parentPairs: IRouteParentPair[];
customHandler?: HandlerFunction;
}

Expand All @@ -363,3 +364,8 @@ export interface IForeignKeyTask {
relation: IRelation;
value: any;
}

export interface IRouteParentPair {
model: IModelService;
paramName: string;
}
15 changes: 8 additions & 7 deletions src/Phases/Show/PreparePhase.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { QueryService } from "../../Services";
import { IContext } from "../../Interfaces";
import { Knex } from "knex";
import { addSoftDeleteQuery } from "../../Handlers/Helpers";

export default async (context: IContext) => {
const { req, model, version, database } = context;

context.queryParser = new QueryService(
context.model,
context.version.modelList.get(),
context.version.config,
model,
version.modelList.get(),
version.config,
);

// We should parse URL query string to use as condition in Lucid query
context.conditions = context.queryParser.get(context.req.query);
context.conditions = context.queryParser.get(req.query);

// Fetching item
context.query = (context.database as Knex).from(context.model.instance.table);
context.query = database.from(model.instance.table);

// If there is a deletedAtColumn, it means that this table support soft-delete
addSoftDeleteQuery(context.model, context.conditions, context.query);
addSoftDeleteQuery(model, context.conditions, context.query);
};
33 changes: 33 additions & 0 deletions src/Phases/URLSearchParamPhase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IContext, IModelService } from "../Interfaces";
import URLService from "../Services/URLService";
import { StatusCodes } from "../Enums";

const queryByPrimaryKey = async (
content: IContext,
queryModel: IModelService,
value: any,
) => {
const { database } = content;
return database
.from(queryModel.instance.table)
.where(queryModel.instance.primaryKey, value)
.first();
};

export default async (context: IContext) => {
const { req, res } = context;

const match = URLService.match(req);
if (match) {
const promises = match.parentPairs.map((pair) =>
queryByPrimaryKey(context, pair.model, match.params[pair.paramName]),
);
const results = await Promise.all(promises);
const isMissing = results.some((item) => !item);
if (isMissing) {
res
.status(StatusCodes.NOT_FOUND)
.json({ error: "The resource is not found" });
}
}
};
4 changes: 4 additions & 0 deletions src/Services/URLService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
IRouteData,
IVersion,
IURLPair,
IRouteParentPair,
} from "../Interfaces";
import AxeRequest from "./AxeRequest";
import { TransactionResolver } from "../Resolvers";
Expand Down Expand Up @@ -53,6 +54,7 @@ class URLService {
pattern: string,
data: IRouteData,
middlewares: AxeFunction[],
parentPairs: IRouteParentPair[],
) {
const phases = this.getDefaultPhases(middlewares);

Expand Down Expand Up @@ -86,6 +88,7 @@ class URLService {
data,
phases,
hasTransaction,
parentPairs,
});
}

Expand Down Expand Up @@ -143,6 +146,7 @@ class URLService {
parentModel: null,
relation: null,
},
parentPairs: [],
});
}

Expand Down
20 changes: 14 additions & 6 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Event from "./Steps/Event";
import ErrorHandler from "./Handlers/ErrorHandler";
import GetCachePhase from "./Phases/GetCachePhase";
import CacheTagCleanPhase from "./Phases/CacheTagCleanPhase";
import URLSearchParamPhase from "./Phases/URLSearchParamPhase";
import { defaultCacheKeyFunction } from "./Handlers/Helpers";

export const RESERVED_KEYWORDS: string[] = [
Expand Down Expand Up @@ -192,6 +193,7 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
],
[HandlerTypes.PAGINATE]: [
new Phase("paginate.cache", GetCachePhase),
new Phase("paginate.URLSearchParamPhase", URLSearchParamPhase),
new Phase("paginate.prepareQuery", Paginate.PreparePhase),
new Hook(HookFunctionTypes.onBeforePaginate),
new Event(HookFunctionTypes.onBeforePaginate),
Expand All @@ -203,7 +205,8 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("paginate.response", List.ResultPhase),
],
[HandlerTypes.SHOW]: [
new Phase("paginate.cache", GetCachePhase),
new Phase("show.cache", GetCachePhase),
new Phase("show.URLSearchParamPhase", URLSearchParamPhase),
new Phase("show.prepareQuery", Show.PreparePhase),
new Hook(HookFunctionTypes.onBeforeShow),
new Event(HookFunctionTypes.onBeforeShow),
Expand All @@ -215,6 +218,7 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("show.response", Single.ResultPhase),
],
[HandlerTypes.UPDATE]: [
new Phase("update.URLSearchParamPhase", URLSearchParamPhase),
new Phase("update.prepareQuery", Single.PrepareGetPhase),
new Hook(HookFunctionTypes.onBeforeUpdateQuery),
new Event(HookFunctionTypes.onBeforeUpdateQuery),
Expand All @@ -227,11 +231,12 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("update.action", Update.ActionPhase),
new Hook(HookFunctionTypes.onAfterUpdate),
new Event(HookFunctionTypes.onAfterUpdate),
new Phase("cache.cleanTags", CacheTagCleanPhase),
new Phase("update.cleanCleanTags", CacheTagCleanPhase),
new Phase("update.serialize", Single.SerializePhase),
new Phase("update.response", Single.ResultPhase),
],
[HandlerTypes.DELETE]: [
new Phase("delete.URLSearchParamPhase", URLSearchParamPhase),
new Phase("delete.prepareQuery", Delete.PreparePhase),
new Hook(HookFunctionTypes.onBeforeDeleteQuery),
new Event(HookFunctionTypes.onBeforeDeleteQuery),
Expand All @@ -243,10 +248,11 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("delete.action", Delete.ActionPhase),
new Hook(HookFunctionTypes.onAfterDelete),
new Event(HookFunctionTypes.onAfterDelete),
new Phase("cache.cleanTags", CacheTagCleanPhase),
new Phase("delete.cleanCleanTags", CacheTagCleanPhase),
new Phase("delete.response", Delete.ResponsePhase),
],
[HandlerTypes.FORCE_DELETE]: [
new Phase("force-delete.URLSearchParamPhase", URLSearchParamPhase),
new Phase("force-delete.prepareQuery", ForceDelete.PreparePhase),
new Hook(HookFunctionTypes.onBeforeForceDeleteQuery),
new Event(HookFunctionTypes.onBeforeForceDeleteQuery),
Expand All @@ -258,10 +264,11 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("force-delete.action", ForceDelete.ActionPhase),
new Hook(HookFunctionTypes.onAfterForceDelete),
new Event(HookFunctionTypes.onAfterForceDelete),
new Phase("cache.cleanTags", CacheTagCleanPhase),
new Phase("force-delete.cleanCleanTags", CacheTagCleanPhase),
new Phase("force-delete.response", Delete.ResponsePhase),
],
[HandlerTypes.PATCH]: [
new Phase("patch.URLSearchParamPhase", URLSearchParamPhase),
new Phase("patch.prepareQuery", Single.PrepareGetPhase),
new Hook(HookFunctionTypes.onBeforeUpdateQuery),
new Event(HookFunctionTypes.onBeforeUpdateQuery),
Expand All @@ -274,12 +281,13 @@ export const HANDLER_CYLES: Record<HandlerTypes, IStepDefinition[]> = {
new Phase("patch.action", Update.ActionPhase),
new Hook(HookFunctionTypes.onAfterUpdate),
new Event(HookFunctionTypes.onAfterUpdate),
new Phase("cache.cleanTags", CacheTagCleanPhase),
new Phase("patch.cleanCleanTags", CacheTagCleanPhase),
new Phase("patch.serialize", Single.SerializePhase),
new Phase("patch.response", Single.ResultPhase),
],
[HandlerTypes.ALL]: [
new Phase("paginate.cache", GetCachePhase),
new Phase("all.cache", GetCachePhase),
new Phase("all.URLSearchParamPhase", URLSearchParamPhase),
new Phase("all.prepareQuery", Paginate.PreparePhase),
new Hook(HookFunctionTypes.onBeforePaginate),
new Event(HookFunctionTypes.onBeforePaginate),
Expand Down

0 comments on commit 64bbc11

Please sign in to comment.