Skip to content

Instantly share code, notes, and snippets.

@thetutlage
Last active April 19, 2024 14:50
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save thetutlage/6cdd19cd0e457033650acad324163e70 to your computer and use it in GitHub Desktop.
Save thetutlage/6cdd19cd0e457033650acad324163e70 to your computer and use it in GitHub Desktop.
Unploy Server API Abstraction for AdonisJS
import Up from './index'
declare module '@ioc:Adonis/Core/HttpContext' {
interface HttpContextContract {
up: Up
}
}
/*
|--------------------------------------------------------------------------
| Http Exception Handler
|--------------------------------------------------------------------------
|
| AdonisJs will forward all exceptions occurred during an HTTP request to
| the following class. You can learn more about exception handling by
| reading docs.
|
| The exception handler extends a base `HttpExceptionHandler` which is not
| mandatory, however it can do lot of heavy lifting to handle the errors
| properly.
|
*/
import Logger from '@ioc:Adonis/Core/Logger'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler'
export default class ExceptionHandler extends HttpExceptionHandler {
protected statusPages = {
'403': 'errors/unauthorized',
'404': 'errors/not-found',
'500..599': 'errors/server-error',
}
constructor() {
super(Logger)
}
public async handle(error: any, ctx: HttpContextContract) {
if (error.code === 'E_VALIDATION_FAILURE') {
ctx.up.setTarget(ctx.up.getFailTarget())
}
return super.handle(error, ctx)
}
}
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
/**
* A list of supported unpoly headers
*/
const UNPOLY_HEADERS = [
'X-Up-Accept-Layer',
'X-Up-Clear-Cache',
'X-Up-Context',
'X-Up-Dismiss-Layer',
'X-Up-Events',
'X-Up-Fail-Context',
'X-Up-Fail-Mode',
'X-Up-Fail-Target',
'X-Up-Location',
'X-Up-Method',
'X-Up-Mode',
'X-Up-Reload-From-Time',
'X-Up-Target',
]
export default class Up {
/**
* The headers to set on the response or flash messages in case of redirect
*/
private headers: Record<string, string> = {}
/**
* Converts the unpoly header name to flash messages key
*/
private headerToFlashKey(header: string) {
return header.replace('X-', '').toLowerCase()
}
/**
* Converts the flash key to header
*/
private flashKeyToHeader(header: string) {
return `X-${header}`
}
/**
* Returns the value of an unpoly header. Giving priority to the
* flash messages
*/
private getProperty(header: string): string {
return this.ctx.session.flashMessages.get(
this.headerToFlashKey(header),
this.ctx.request.header(header)
)
}
/**
* Set the unpoly response header
*/
private setProperty(header: string, value: string): void {
this.headers[header] = value
}
/**
* Set unpoly headers as flash messages
*/
private setHeadersAsFlashMessages(headers: Record<string, string>) {
Object.keys(headers).forEach((header) => {
this.ctx.session.flash(this.headerToFlashKey(header), headers[header])
})
}
/**
* Set unpoly headers as response headers
*/
private setHeadersAsResponse(headers: Record<string, string>) {
Object.keys(headers).forEach((header) => {
this.ctx.response.header(header, headers[header])
})
}
constructor(private ctx: HttpContextContract) {}
/**
* Commit response
*/
public commit() {
const headers = Object.assign(
UNPOLY_HEADERS.reduce((result, header) => {
const value = this.ctx.session.flashMessages.get(this.headerToFlashKey(header))
if (value) {
result[header] = value
}
return result
}, {}),
this.headers
)
if (this.ctx.response.getHeader('Location')) {
this.setHeadersAsFlashMessages(headers)
} else {
this.setHeadersAsResponse(headers)
}
}
public getLayer() {
return this.getProperty('X-Up-Accept-Layer')
}
public getCache() {
return this.getProperty('X-Up-Clear-Cache')
}
public getContext() {
return this.getProperty('X-Up-Context')
}
public getDismissLayer() {
return this.getProperty('X-Up-Dismiss-Layer')
}
public getEvents() {
return this.getProperty('X-Up-Events')
}
public getFailContext() {
return this.getProperty('X-Up-Fail-Context')
}
public getFailMode() {
return this.getProperty('X-Up-Fail-Mode')
}
public getFailTarget() {
return this.getProperty('X-Up-Fail-Target')
}
public getLocation() {
return this.getProperty('X-Up-Location')
}
public getMethod() {
return this.getProperty('X-Up-Method')
}
public getMode() {
return this.getProperty('X-Up-Mode')
}
public getReloadFromTime() {
return this.getProperty('X-Up-Reload-From-Time')
}
public getTarget() {
return this.getProperty('X-Up-Target') || 'body'
}
public targetIncludes(selector: string): boolean {
const target = this.getTarget()
.split(',')
.map((value) => value.trim())
return target.includes('body') ? true : target.includes(selector)
}
public getTitle() {
return this.getProperty('X-Up-Title')
}
public getValidate() {
return this.getProperty('X-Up-Validate')
}
public getVersion() {
return this.getProperty('X-Up-Version')
}
public setLayer(value: string) {
return this.setProperty('X-Up-Accept-Layer', value)
}
public setCache(value: string) {
return this.setProperty('X-Up-Clear-Cache', value)
}
public setContext(value: string) {
return this.setProperty('X-Up-Context', value)
}
public setDismissLayer(value: string) {
return this.setProperty('X-Up-Dismiss-Layer', value)
}
public setEvents(value: string) {
return this.setProperty('X-Up-Events', value)
}
public setFailContext(value: string) {
return this.setProperty('X-Up-Fail-Context', value)
}
public setFailMode(value: string) {
return this.setProperty('X-Up-Fail-Mode', value)
}
public setFailTarget(value: string) {
return this.setProperty('X-Up-Fail-Target', value)
}
public setLocation(value: string) {
return this.setProperty('X-Up-Location', value)
}
public setMethod(value: string) {
return this.setProperty('X-Up-Method', value)
}
public setMode(value: string) {
return this.setProperty('X-Up-Mode', value)
}
public setReloadFromTime(value: string) {
return this.setProperty('X-Up-Reload-From-Time', value)
}
public setTarget(value: string) {
return this.setProperty('X-Up-Target', value)
}
public setTitle(value: string) {
return this.setProperty('X-Up-Title', value)
}
public setValidate(value: string) {
return this.setProperty('X-Up-Validate', value)
}
public setVersion(value: string) {
return this.setProperty('X-Up-Version', value)
}
}
import Up from './index'
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class AppProvider {
constructor(protected app: ApplicationContract) {}
public async boot() {
const HttpContext = this.app.container.resolveBinding('Adonis/Core/HttpContext')
const Server = this.app.container.resolveBinding('Adonis/Core/Server')
HttpContext.getter(
'up',
function () {
return new Up(this)
},
true
)
Server.hooks.before(async (ctx) => {
ctx.view.share({ up: ctx.up })
})
Server.hooks.after(async (ctx) => {
ctx.up.commit()
})
}
}
@azurha
Copy link

azurha commented Apr 19, 2024

Hi, I'm a big fan of adonis and unpoly.
Is a version of this helper for adonisV6 planned?
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment