Skip to content

Commit

Permalink
Merge pull request #467 from instantcommerce/feat/v2
Browse files Browse the repository at this point in the history
v2
  • Loading branch information
ggurkal authored Aug 18, 2022
2 parents da2c966 + 653a4c9 commit 4d8010a
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 55 deletions.
8 changes: 6 additions & 2 deletions lib/createHandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'reflect-metadata';
import express from 'express';
import request from 'supertest';
import { createHandler } from './createHandler';
import { HttpVerb, HTTP_METHOD_TOKEN } from './decorators';
import { HttpMethod, HTTP_METHOD_TOKEN } from './decorators';

describe('createHandler', () => {
it('Should return not found response when "req.url" is undefined', () => {
Expand All @@ -22,7 +22,11 @@ describe('createHandler', () => {

it('Should return not found when method is not found.', done => {
class TestHandler {}
Reflect.defineMetadata(HTTP_METHOD_TOKEN, [{ path: '/', verb: HttpVerb.GET, propertyKey: 'index' }], TestHandler);
Reflect.defineMetadata(
HTTP_METHOD_TOKEN,
[{ path: '/', method: HttpMethod.GET, propertyKey: 'index' }],
TestHandler
);

const server = express();
server.use(express.json());
Expand Down
2 changes: 1 addition & 1 deletion lib/decorators/catch.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ClassConstructor } from 'class-transformer';
import type { NextApiRequest, NextApiResponse } from 'next';

export const CATCH_TOKEN = Symbol('ams:next:catch');
export const CATCH_TOKEN = Symbol('instant:next:catch');

type ExceptionHandlerFunction<T> = (error: T, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;

Expand Down
2 changes: 1 addition & 1 deletion lib/decorators/download.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const HTTP_DOWNLOAD_TOKEN = Symbol('ams:next:download');
export const HTTP_DOWNLOAD_TOKEN = Symbol('instant:next:download');

/**
* Marks the method as a download handler for the client, so the returned file can be downloaded by the browser.
Expand Down
2 changes: 1 addition & 1 deletion lib/decorators/httpCode.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const HTTP_CODE_TOKEN = Symbol('ams:next:httpCode');
export const HTTP_CODE_TOKEN = Symbol('instant:next:httpCode');

/**
* Defines the HTTP response code of the route.
Expand Down
30 changes: 17 additions & 13 deletions lib/decorators/httpMethod.decorators.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import 'reflect-metadata';
import * as lp from '../internals/loadPackage';
import { Get, Post, Put, Delete, HttpVerb, HTTP_METHOD_TOKEN, Patch } from './httpMethod.decorators';
import { Get, Post, Put, Delete, HttpMethod, HTTP_METHOD_TOKEN, Patch } from './httpMethod.decorators';

class Test {
@Get()
Expand Down Expand Up @@ -49,30 +49,34 @@ describe('HttpMethod decorator', () => {
process.env = ENV;
});

it('Should create all the verbs.', () => {
it('Should create all the methods.', () => {
const meta = Reflect.getMetadata(HTTP_METHOD_TOKEN, Test);
expect(meta).toBeInstanceOf(Array);
expect(meta).toMatchObject(
expect.arrayContaining([
{ path: '/', verb: HttpVerb.GET, propertyKey: 'get' },
{ path: '/', verb: HttpVerb.POST, propertyKey: 'post' },
{ path: '/', verb: HttpVerb.PUT, propertyKey: 'put' },
{ path: '/', verb: HttpVerb.DELETE, propertyKey: 'delete' },
{ path: '/', verb: HttpVerb.PATCH, propertyKey: 'patch' }
{ path: '/', method: HttpMethod.GET, propertyKey: 'get' },
{ path: '/', method: HttpMethod.POST, propertyKey: 'post' },
{ path: '/', method: HttpMethod.PUT, propertyKey: 'put' },
{ path: '/', method: HttpMethod.DELETE, propertyKey: 'delete' },
{ path: '/', method: HttpMethod.PATCH, propertyKey: 'patch' }
])
);
});

it('Should create the GET verb with paths', () => {
it('Should create the GET method with paths', () => {
const meta = Reflect.getMetadata(HTTP_METHOD_TOKEN, TestPath);
expect(meta).toBeInstanceOf(Array);
expect(meta).toMatchObject(
expect.arrayContaining([
{ path: '/', verb: HttpVerb.GET, propertyKey: 'index' },
{ path: '/explore', verb: HttpVerb.GET, propertyKey: 'explore' },
{ path: '/explore/:id', verb: HttpVerb.GET, propertyKey: 'exploreDetails' },
{ path: '/explore/:id/comments', verb: HttpVerb.GET, propertyKey: 'exploreDetailsComments' },
{ path: '/explore/:id/comments/:commentId', verb: HttpVerb.GET, propertyKey: 'exploreDetailsCommentDetails' }
{ path: '/', method: HttpMethod.GET, propertyKey: 'index' },
{ path: '/explore', method: HttpMethod.GET, propertyKey: 'explore' },
{ path: '/explore/:id', method: HttpMethod.GET, propertyKey: 'exploreDetails' },
{ path: '/explore/:id/comments', method: HttpMethod.GET, propertyKey: 'exploreDetailsComments' },
{
path: '/explore/:id/comments/:commentId',
method: HttpMethod.GET,
propertyKey: 'exploreDetailsCommentDetails'
}
])
);
});
Expand Down
81 changes: 64 additions & 17 deletions lib/decorators/httpMethod.decorators.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,61 @@
import { applyHandler } from '../internals/handler';
import { loadPackage } from '../internals/loadPackage';

export enum HttpVerb {
export enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
PATCH = 'PATCH'
PATCH = 'PATCH',
OPTIONS = 'OPTIONS',
HEAD = 'HEAD',
CONNECT = 'CONNECT',
TRACE = 'TRACE'
}

export interface HandlerMethod {
verb: HttpVerb;
method: HttpMethod;
options?: HandlerOptions;
path: string;
propertyKey: string | symbol;
}

export const HTTP_METHOD_TOKEN = Symbol('ams:next:httpMethod');
interface HandlerOptions {
extraMethods?: HttpMethod[];
}

export const HTTP_METHOD_TOKEN = Symbol('instant:next:httpMethod');

function applyHttpMethod(verb: HttpVerb, path: string) {
function applyHttpMethod({ method, path, options }: { method: HttpMethod; path: string; options?: HandlerOptions }) {
if (process.env.NODE_ENV === 'development' && path !== '/') {
loadPackage('path-to-regexp', {
context: '@' + verb.charAt(0).toUpperCase() + verb.slice(1).toLowerCase(),
context: '@' + method.charAt(0).toUpperCase() + method.slice(1).toLowerCase(),
docsUrl: 'https://next-api-decorators.vercel.app/docs/routing/route-matching'
});
}

return function (target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) {
const methods: Array<HandlerMethod> = Reflect.getMetadata(HTTP_METHOD_TOKEN, target.constructor) ?? [];

methods.push({ path, verb, propertyKey });
methods.push({ path, method, options, propertyKey });

Reflect.defineMetadata(HTTP_METHOD_TOKEN, methods, target.constructor);

return applyHandler(target, propertyKey, descriptor);
};
}

function getPath(pathOrOptions?: string | HandlerOptions) {
return typeof pathOrOptions === 'string' ? pathOrOptions : '/';
}

function getOptions(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions) {
return typeof pathOrOptions === 'object' ? pathOrOptions : options;
}

/** Makes the method a GET request handler. */
export function Get(): MethodDecorator;
export function Get(options: HandlerOptions): MethodDecorator;
/**
* Makes the method for the defined path a GET request handler.
*
Expand All @@ -49,12 +67,18 @@ export function Get(): MethodDecorator;
* More information: [route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)
*/
export function Get(path: string): MethodDecorator;
export function Get(path: string = '/'): MethodDecorator {
return applyHttpMethod(HttpVerb.GET, path);
export function Get(path: string, options: HandlerOptions): MethodDecorator;
export function Get(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions): MethodDecorator {
return applyHttpMethod({
method: HttpMethod.GET,
path: getPath(pathOrOptions),
options: getOptions(pathOrOptions, options)
});
}

/** Makes the method a POST request handler. */
export function Post(): MethodDecorator;
export function Post(options: HandlerOptions): MethodDecorator;
/**
* Makes the method for the defined path a POST request handler.
*
Expand All @@ -66,12 +90,18 @@ export function Post(): MethodDecorator;
* More information: [route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)
*/
export function Post(path: string): MethodDecorator;
export function Post(path: string = '/'): MethodDecorator {
return applyHttpMethod(HttpVerb.POST, path);
export function Post(path: string, options: HandlerOptions): MethodDecorator;
export function Post(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions): MethodDecorator {
return applyHttpMethod({
method: HttpMethod.POST,
path: getPath(pathOrOptions),
options: getOptions(pathOrOptions, options)
});
}

/** Makes the method a PUT request handler. */
export function Put(): MethodDecorator;
export function Put(options: HandlerOptions): MethodDecorator;
/**
* Makes the method for the defined path a PUT request handler.
*
Expand All @@ -83,12 +113,18 @@ export function Put(): MethodDecorator;
* More information: [route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)
*/
export function Put(path: string): MethodDecorator;
export function Put(path: string = '/'): MethodDecorator {
return applyHttpMethod(HttpVerb.PUT, path);
export function Put(path: string, options: HandlerOptions): MethodDecorator;
export function Put(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions): MethodDecorator {
return applyHttpMethod({
method: HttpMethod.PUT,
path: getPath(pathOrOptions),
options: getOptions(pathOrOptions, options)
});
}

/** Makes the method a DELETE request handler. */
export function Delete(): MethodDecorator;
export function Delete(options: HandlerOptions): MethodDecorator;
/**
* Makes the method for the defined path a DELETE request handler.
*
Expand All @@ -100,12 +136,18 @@ export function Delete(): MethodDecorator;
* More information: [route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)
*/
export function Delete(path: string): MethodDecorator;
export function Delete(path: string = '/'): MethodDecorator {
return applyHttpMethod(HttpVerb.DELETE, path);
export function Delete(path: string, options: HandlerOptions): MethodDecorator;
export function Delete(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions): MethodDecorator {
return applyHttpMethod({
method: HttpMethod.DELETE,
path: getPath(pathOrOptions),
options: getOptions(pathOrOptions, options)
});
}

/** Makes the method a PATCH request handler. */
export function Patch(): MethodDecorator;
export function Patch(options: HandlerOptions): MethodDecorator;
/**
* Makes the method for the defined path a PATCH request handler.
*
Expand All @@ -117,6 +159,11 @@ export function Patch(): MethodDecorator;
* More information: [route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)
*/
export function Patch(path: string): MethodDecorator;
export function Patch(path: string = '/'): MethodDecorator {
return applyHttpMethod(HttpVerb.PATCH, path);
export function Patch(path: string, options: HandlerOptions): MethodDecorator;
export function Patch(pathOrOptions?: string | HandlerOptions, options?: HandlerOptions): MethodDecorator {
return applyHttpMethod({
method: HttpMethod.PATCH,
path: getPath(pathOrOptions),
options: getOptions(pathOrOptions, options)
});
}
2 changes: 1 addition & 1 deletion lib/decorators/middleware.decorators.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RequestHandler } from 'express';
import type { NextApiRequest, NextApiResponse } from 'next';

export const MIDDLEWARE_TOKEN = Symbol('ams:next:middlewares');
export const MIDDLEWARE_TOKEN = Symbol('instant:next:middlewares');

export type NextFunction = (err?: Error) => void;
export type NextMiddleware = (req: NextApiRequest, res: NextApiResponse, next: NextFunction) => void | Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion lib/decorators/parameter.decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface MetaParameter {
fn?: ParamDecorator<any>;
}

export const PARAMETER_TOKEN = Symbol('ams:next:parameters');
export const PARAMETER_TOKEN = Symbol('instant:next:parameters');

function addParameter(
location: MetaParameter['location'],
Expand Down
2 changes: 1 addition & 1 deletion lib/decorators/setHeader.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const HEADER_TOKEN = Symbol('ams:next:header');
export const HEADER_TOKEN = Symbol('instant:next:header');

/**
* Sets a header parameter into the response header.
Expand Down
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export {
Get,
Header,
HttpCode,
HttpVerb,
HttpMethod,
Post,
Put,
Query,
Expand Down
Loading

0 comments on commit 4d8010a

Please # to comment.