Skip to content

Commit

Permalink
The All decorator has been added and we can use this decorator to man…
Browse files Browse the repository at this point in the history
…age graphql requests like the all method in glands itself.

Added nxt type
  • Loading branch information
m-mdy-m committed Aug 27, 2024
1 parent 2abb82b commit 3022841
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 38 deletions.
10 changes: 10 additions & 0 deletions examples/CRUD/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/CRUD/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@types/node": "^22.5.0",
"glands": "^1.0.3",
"graphql": "^16.9.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
Expand Down
42 changes: 40 additions & 2 deletions examples/CRUD/router/crud.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
import { exposed, Get, Route, Delete, Post, Put, Context } from '../../../dist';
import { graphql, buildSchema } from 'graphql';
import { exposed, Route, Context, All, Get, Post, Delete, Put } from '../../../dist';
// Define a simple GraphQL schema
const schema = buildSchema(`
type Query {
hello(name: String): String
}
`);

// Define the root resolver
const root = {
hello: ({ name }: { name: string }) => `Hello, ${name || 'world'}!`,
};


@Route('/graphql')
@exposed
class GraphQLHandler {
@All()
async handleGraphQL(ctx: Context) {
const { query, variables } = ctx.body as { query: string; variables?: any };

// Execute the GraphQL query
const result = await graphql({
schema,
source: query,
variableValues: variables,
rootValue: root,
});

// Send the result back as the response
ctx.writeHead(200, {
'Content-Type': 'application/json',
});
ctx.write(JSON.stringify(result));
ctx.end();
}
}

import p from 'path';
import * as fs from 'fs';
const db = p.join(__dirname, '..', 'db', 'index.json');
Expand Down Expand Up @@ -99,4 +137,4 @@ class CRUD {

ctx.end();
}
}
}
34 changes: 20 additions & 14 deletions lib/core/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ function generator(method: string) {
};
};
}
export const All = function allGenerator(): MethodDecorator | any {
return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>): void => {
Reflect.init('method', 'ALL', target, propertyKey);
Reflect.init('path', '*', target, propertyKey);
};
};
export const { Get, Post, Put, Delete, Patch, Options, Head } = methods;

export namespace Router {
export function set(controllerClassOrFunction: RouteHandler, routePath?: string): void {
if (typeof controllerClassOrFunction === 'function') {
Expand All @@ -40,29 +45,34 @@ export namespace Router {
}
export function findMatch(path: string, method: string, base: string): { controller: any; handlerKey: string; fullRoutePath: string; params: Record<string, string> } | null {
for (const [routePath, controller] of routes.entries()) {
console.log('routePath:', routePath);
console.log('path:', path);
if (path.startsWith(routePath)) {
if (isClass(controller)) {
const routeInstance = new controller();
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(routeInstance)).filter((key) => key !== 'constructor');
console.log('KEYS:', keys);

for (const key of keys) {
const handlerMethod = Reflect.get('method', controller.prototype, key);
const handlerPath = Reflect.get('path', controller.prototype, key);
console.log('handlerPath:', handlerPath);
console.log('handlerMethod:', handlerMethod);
let fullRoutePath = handlerPath ? `${routePath}${handlerPath}` : routePath;
const parsedURL = new Parser.URI(path, base, fullRoutePath);

if (handlerPath && handlerPath.startsWith('/:')) {
const paramName = handlerPath.split(':')[1];
fullRoutePath = `${routePath}/${parsedURL.params[paramName]}`;
}

if (handlerMethod === method && fullRoutePath === path) {
return { controller, handlerKey: key, fullRoutePath, params: parsedURL.params };
if ((handlerMethod === method || handlerMethod === 'ALL') && fullRoutePath === path) {
return {
controller,
handlerKey: key,
fullRoutePath,
params: parsedURL.params,
};
} else if (handlerMethod === 'ALL' && fullRoutePath.startsWith(path)) {
return {
controller,
handlerKey: key,
fullRoutePath,
params: parsedURL.params,
};
}
}
} else if (typeof controller === 'function') {
Expand All @@ -85,10 +95,6 @@ export namespace Router {
const globalMids = Gmids.get();

const allMids = [...globalMids, ...classMids, ...methodMids];
// Parse JSON body if the method is POST, PUT, or PATCH
if (['POST', 'PUT', 'PATCH'].includes(ctx.method!)) {
ctx.body = await ctx.json();
}
// Execute global middlewares first
await execute(ctx, GlMid);
// Execute class and method middlewares with the handler
Expand Down
55 changes: 39 additions & 16 deletions lib/core/server.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { IncomingMessage, Server, ServerResponse, METHODS } from 'http';
import { Parser } from '../helper/parser';
import { Gland } from '../types';
import { Gland, ListenArgs, NxtFunction } from '../types';
import { ServerUtils } from '../helper';
import { WebContext } from './context';
import { Router } from './router';
import { LoadModules } from '../helper/load';
import { Context } from '../types';
import { midManager } from './middleware';
export class WebServer extends Server implements Gland.Listener, Gland.APP {
export class WebServer extends Server implements Gland.APP {
private middlewares: Gland.Middleware[] = [];
constructor() {
super();
Expand All @@ -25,7 +25,7 @@ export class WebServer extends Server implements Gland.Listener, Gland.APP {
const isAlreadyAdded = this.middlewares.some((middleware) => (middleware as any).key === middlewareKey);

if (!isAlreadyAdded) {
const middleware = async (ctx: Context, next: () => Promise<void>) => {
const middleware = async (ctx: Context, next: NxtFunction) => {
if (ctx.url!.startsWith(path) && ctx.method === method) {
await handler(ctx);
if (ctx.writableEnded) {
Expand Down Expand Up @@ -53,6 +53,7 @@ export class WebServer extends Server implements Gland.Listener, Gland.APP {
const { controller, handlerKey, params } = matchingRoute;
ctx.query = Object.fromEntries(url.searchParams.entries());
ctx.params = params;
ctx.body = await ctx.json();
// Check if the controller is a class or a function
if (typeof controller === 'function' && !handlerKey) {
const middlewareStack = Array.from(new Set([...this.middlewares, controller]));
Expand All @@ -64,26 +65,48 @@ export class WebServer extends Server implements Gland.Listener, Gland.APP {
}
}
}
init(opts: Gland.ListenOptions = {}, listeningListener?: (info: Gland.ListenOptions) => void): Server {
// Assign default values if not provided by the user
opts.port ??= 3000;
opts.host ??= 'localhost';
opts.logger ??= true;
this.on('request', this.lifecycle.bind(this));
listen(...args: ListenArgs): this {
let port: number | undefined;
let host: string | undefined;
let backlog: number | undefined;
let listener: (() => void) | undefined;

if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Function)) {
const opts = args[0] as Gland.ListenOptions;
port = opts.port ?? 3000;
host = opts.host ?? 'localhost';
backlog = opts.backlog;
listener = args[1] as (() => void) | undefined;

const listener = ServerUtils.Tools.listener(opts, listeningListener);
if (opts.logger) {
ServerUtils.Tools.log(opts);
if (opts.logger) {
ServerUtils.Tools.log(opts);
}
} else {
if (typeof args[0] === 'number') port = args[0];
if (typeof args[1] === 'string') host = args[1];
if (typeof args[1] === 'number') backlog = args[1];
if (typeof args[2] === 'number') backlog = args[2];
if (typeof args[1] === 'function') listener = args[1];
if (typeof args[2] === 'function') listener = args[2];
if (typeof args[3] === 'function') listener = args[3];
}
if (opts.path) {
this.listen(opts.path, opts.backlog, listener);

this.on('request', this.lifecycle.bind(this));

if (typeof args[0] === 'string') {
super.listen(args[0], backlog, listener);
} else {
this.listen(opts.port, opts.host, opts.backlog, listener);
super.listen(port, host, backlog, listener);
}

return this;
}

// Reuse ListenArgs type for init method
init(...args: ListenArgs): this {
return this.listen(...args);
}
async load(paths: string = './*.ts') {
await LoadModules.load(paths);
}
}
export const g = new WebServer();
4 changes: 2 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Qiu } from './cli/Qiu';
import { DbTypes, Context } from './types';
import { Logger } from './helper/logger';
import { exposed, Route } from './core/decorators';
import { Delete, Get, Head, Options, Patch, Post, Put } from './core/router/index';
import { Delete, Get, Head, Options, Patch, Post, Put, All } from './core/router/index';
import { NxtFunction } from './types/index';
import { mid, mids } from './core/decorators/index';
import { Gmids } from './core/middleware';
Expand All @@ -19,5 +19,5 @@ export default class gland extends WebServer {
return Logger;
}
}
export { Get, Post, Put, Delete, Patch, Head, Options, Route, exposed, mid, mids };
export { Get, Post, Put, Delete, Patch, Head, Options, Route, exposed, mid, mids, All };
export var Gmid = Gmids.set;
11 changes: 8 additions & 3 deletions lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ export namespace Gland {
exclusive?: boolean;
logger?: boolean;
}
export interface Listener {
init(opts: ListenOptions): void;
}
export interface StaticOptions {
dotfiles?: 'allow' | 'deny' | 'ignore';
etag?: boolean;
Expand Down Expand Up @@ -58,3 +55,11 @@ export type MidsFn = (ctx: Context, next: NxtFunction) => any;
export type RouteHandler = new (...args: any[]) => any | ((...args: any[]) => any);
export type DbTypes = 'mariadb' | 'postgres' | 'sqlite' | 'sqlserver' | 'mysql';
export type NxtFunction = () => Promise<void>;
export type ListenArgs =
| [port?: number, hostname?: string, backlog?: number, listeningListener?: () => void]
| [port?: number, hostname?: string, listeningListener?: () => void]
| [port?: number, backlog?: number, listeningListener?: () => void]
| [port?: number, listeningListener?: () => void]
| [path: string, backlog?: number, listeningListener?: () => void]
| [path: string, listeningListener?: () => void]
| [options: Gland.ListenOptions, listeningListener?: () => void];
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "glands",
"version": "1.0.5",
"version": "1.0.6",
"description": "Glands is a lightweight framework for Node.js designed for simplicity and high performance.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down

0 comments on commit 3022841

Please sign in to comment.