diff --git a/app/http/controllers/Api.controller.ts b/app/http/controllers/Api.controller.ts new file mode 100644 index 0000000..3fe2254 --- /dev/null +++ b/app/http/controllers/Api.controller.ts @@ -0,0 +1,25 @@ +import BaseApiController from "../../../lib/src/http/ApiController.ts"; +import {Request} from "../../../lib/src/http/Request.ts"; +import {redirect} from "../../../lib/src/http/response/helpers.ts"; + +export default class ApiController extends BaseApiController { + + get_good(request: Request) { + return { + session_key: request.session.get_key(), + } + } + + get_arr(request: Request) { + return ['alpha', 'bravo', 'charlie', 'delta', 'echo'] + } + + get_msg(request: Request) { + return 'Hello, world!' + } + + get_redirect(request: Request) { + return redirect('/home') + } + +} diff --git a/lib/src/http/ApiController.ts b/lib/src/http/ApiController.ts new file mode 100644 index 0000000..2642316 --- /dev/null +++ b/lib/src/http/ApiController.ts @@ -0,0 +1,41 @@ +import Controller from "./Controller.ts"; +import {Request} from "./Request.ts"; +import ResponseFactory from "./response/ResponseFactory.ts"; +import * as api from '../support/api.ts' +import JSONResponseFactory from "./response/JSONResponseFactory.ts"; + +export default class ApiController extends Controller { + public get_bound_method(method_name: string): (...args: any[]) => any { + // @ts-ignore + if ( typeof this[method_name] !== 'function' ) { + throw new TypeError(`Attempt to get bound method for non-function type: ${method_name}`) + } + + return async (...args: any[]): Promise => { + if ( args.length !== 1 ) { + // @ts-ignore + return this[method_name](...args) + } + + const request: Request = args[0] + // @ts-ignore + const result = await this[method_name](request) + + // If we have a request or a response factory, return that + if ( result instanceof Request || result instanceof ResponseFactory ) { + return result + } + + console.log('result', result) + + // Otherwise, try to package the results as an API response + if ( Array.isArray(result) ) { + return this.make(JSONResponseFactory, api.many(result)) + } else if ( typeof result === 'string' ) { + return this.make(JSONResponseFactory, api.message(result)) + } else { + return this.make(JSONResponseFactory, api.one(result)) + } + } + } +} diff --git a/lib/src/http/response/ErrorResponseFactory.ts b/lib/src/http/response/ErrorResponseFactory.ts index ec47eb3..62fbacf 100644 --- a/lib/src/http/response/ErrorResponseFactory.ts +++ b/lib/src/http/response/ErrorResponseFactory.ts @@ -1,5 +1,6 @@ import ResponseFactory from './ResponseFactory.ts' import {Request} from '../Request.ts' +import * as api from '../../support/api.ts' export default class ErrorResponseFactory extends ResponseFactory { protected target_mode: 'json' | 'html' = 'html' @@ -22,10 +23,10 @@ export default class ErrorResponseFactory extends ResponseFactory { public async write(request: Request): Promise { request = await super.write(request) - if ( this.output === 'json' ) { + if ( this.target_mode === 'json' ) { request.response.headers.set('Content-Type', 'application/json') request.response.body = this.build_json(this.error) - } else if ( this.output === 'html' ) { + } else if ( this.target_mode === 'html' ) { request.response.headers.set('Content-Type', 'text/html') request.response.body = this.build_html(this.error) } @@ -47,13 +48,6 @@ Stack trace: } protected build_json(error: Error) { - return JSON.stringify({ - success: false, - error: { - name: error.name, - message: error.message, - stack: error.stack ? error.stack.split(/\s+at\s+/).slice(1) : [] - } - }) + return JSON.stringify(api.error(error)) } } diff --git a/lib/src/http/response/TemporaryRedirectResponseFactory.ts b/lib/src/http/response/TemporaryRedirectResponseFactory.ts index 03f8a66..472eb2c 100644 --- a/lib/src/http/response/TemporaryRedirectResponseFactory.ts +++ b/lib/src/http/response/TemporaryRedirectResponseFactory.ts @@ -2,7 +2,7 @@ import ResponseFactory from './ResponseFactory.ts' import {Request} from '../Request.ts' export default class TemporaryRedirectResponseFactory extends ResponseFactory { - protected target_status: number = 301 + protected target_status: number = 302 constructor( public readonly destination: string, diff --git a/lib/src/http/routing/ComplexRoute.ts b/lib/src/http/routing/ComplexRoute.ts index 8f2bb9f..143771f 100644 --- a/lib/src/http/routing/ComplexRoute.ts +++ b/lib/src/http/routing/ComplexRoute.ts @@ -4,9 +4,14 @@ import {make} from '../../../../di/src/global.ts' export class ComplexRoute extends Route { public match(incoming: string): boolean { - const base_length = this.split(this.base).length - const incoming_length = this.split(incoming).length - return base_length === incoming_length // FIXME match! + const base_parts = this.split(this.base) + const incoming_parts = this.split(incoming) + + if ( base_parts.length !== incoming_parts.length ) return false + + return base_parts.every((base, idx) => { + return base.toLowerCase() === incoming_parts[idx].toLowerCase() + }) } public build_parameters(incoming: string): RouteParameters { diff --git a/lib/src/http/routing/SimpleRoute.ts b/lib/src/http/routing/SimpleRoute.ts index de04ff9..f832625 100644 --- a/lib/src/http/routing/SimpleRoute.ts +++ b/lib/src/http/routing/SimpleRoute.ts @@ -2,7 +2,7 @@ import {Route, RouteParameters} from './Route.ts' export class SimpleRoute extends Route { public match(incoming: string): boolean { - return incoming === this.base + return incoming.toLowerCase() === this.base.toLowerCase() } public build_parameters(incoming: string): RouteParameters { diff --git a/lib/src/support/api.ts b/lib/src/support/api.ts new file mode 100644 index 0000000..139997a --- /dev/null +++ b/lib/src/support/api.ts @@ -0,0 +1,53 @@ +export interface APIResponse { + success: boolean, + message?: string, + data?: any, + error?: { + name: string, + message: string, + stack?: string[], + } +} + +export function message(message: string): APIResponse { + return { + success: true, + message, + } +} + +export function one(record: any): APIResponse { + return { + success: true, + data: record, + } +} + +export function many(records: any[]): APIResponse { + return { + success: true, + data: { + records, + total: records.length, + }, + } +} + +export function error(error: string | Error): APIResponse { + if ( typeof error === 'string' ) { + return { + success: false, + message: error, + } + } else { + return { + success: false, + message: error.message, + error: { + name: error.name, + message: error.message, + stack: error.stack ? error.stack.split(/\s+at\s+/).slice(1) : [], + }, + } + } +}