Fix complex route matching; add api controller; api standard helpers
This commit is contained in:
parent
0764085ef9
commit
0e5a0a5a5c
25
app/http/controllers/Api.controller.ts
Normal file
25
app/http/controllers/Api.controller.ts
Normal file
@ -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')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
lib/src/http/ApiController.ts
Normal file
41
lib/src/http/ApiController.ts
Normal file
@ -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<any> => {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import ResponseFactory from './ResponseFactory.ts'
|
import ResponseFactory from './ResponseFactory.ts'
|
||||||
import {Request} from '../Request.ts'
|
import {Request} from '../Request.ts'
|
||||||
|
import * as api from '../../support/api.ts'
|
||||||
|
|
||||||
export default class ErrorResponseFactory extends ResponseFactory {
|
export default class ErrorResponseFactory extends ResponseFactory {
|
||||||
protected target_mode: 'json' | 'html' = 'html'
|
protected target_mode: 'json' | 'html' = 'html'
|
||||||
@ -22,10 +23,10 @@ export default class ErrorResponseFactory extends ResponseFactory {
|
|||||||
public async write(request: Request): Promise<Request> {
|
public async write(request: Request): Promise<Request> {
|
||||||
request = await super.write(request)
|
request = await super.write(request)
|
||||||
|
|
||||||
if ( this.output === 'json' ) {
|
if ( this.target_mode === 'json' ) {
|
||||||
request.response.headers.set('Content-Type', 'application/json')
|
request.response.headers.set('Content-Type', 'application/json')
|
||||||
request.response.body = this.build_json(this.error)
|
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.headers.set('Content-Type', 'text/html')
|
||||||
request.response.body = this.build_html(this.error)
|
request.response.body = this.build_html(this.error)
|
||||||
}
|
}
|
||||||
@ -47,13 +48,6 @@ Stack trace:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected build_json(error: Error) {
|
protected build_json(error: Error) {
|
||||||
return JSON.stringify({
|
return JSON.stringify(api.error(error))
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
name: error.name,
|
|
||||||
message: error.message,
|
|
||||||
stack: error.stack ? error.stack.split(/\s+at\s+/).slice(1) : []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import ResponseFactory from './ResponseFactory.ts'
|
|||||||
import {Request} from '../Request.ts'
|
import {Request} from '../Request.ts'
|
||||||
|
|
||||||
export default class TemporaryRedirectResponseFactory extends ResponseFactory {
|
export default class TemporaryRedirectResponseFactory extends ResponseFactory {
|
||||||
protected target_status: number = 301
|
protected target_status: number = 302
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly destination: string,
|
public readonly destination: string,
|
||||||
|
@ -4,9 +4,14 @@ import {make} from '../../../../di/src/global.ts'
|
|||||||
|
|
||||||
export class ComplexRoute extends Route {
|
export class ComplexRoute extends Route {
|
||||||
public match(incoming: string): boolean {
|
public match(incoming: string): boolean {
|
||||||
const base_length = this.split(this.base).length
|
const base_parts = this.split(this.base)
|
||||||
const incoming_length = this.split(incoming).length
|
const incoming_parts = this.split(incoming)
|
||||||
return base_length === incoming_length // FIXME match!
|
|
||||||
|
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 {
|
public build_parameters(incoming: string): RouteParameters {
|
||||||
|
@ -2,7 +2,7 @@ import {Route, RouteParameters} from './Route.ts'
|
|||||||
|
|
||||||
export class SimpleRoute extends Route {
|
export class SimpleRoute extends Route {
|
||||||
public match(incoming: string): boolean {
|
public match(incoming: string): boolean {
|
||||||
return incoming === this.base
|
return incoming.toLowerCase() === this.base.toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
public build_parameters(incoming: string): RouteParameters {
|
public build_parameters(incoming: string): RouteParameters {
|
||||||
|
53
lib/src/support/api.ts
Normal file
53
lib/src/support/api.ts
Normal file
@ -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) : [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user