Skip to content

Commit 09e813a

Browse files
njlieNathan Lie
and
Nathan Lie
authored
feat(backend): mount resource server routes explicitly (interledger#1884)
Co-authored-by: Nathan Lie <[email protected]>
1 parent d8881ed commit 09e813a

File tree

1 file changed

+209
-108
lines changed

1 file changed

+209
-108
lines changed

packages/backend/src/app.ts

+209-108
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,29 @@ import {
3434
RequestAction
3535
} from './open_payments/auth/middleware'
3636
import { RatesService } from './rates/service'
37-
import { spspMiddleware } from './spsp/middleware'
37+
import { spspMiddleware, SPSPConnectionContext } from './spsp/middleware'
3838
import { SPSPRoutes } from './spsp/routes'
39-
import { IncomingPaymentRoutes } from './open_payments/payment/incoming/routes'
39+
import {
40+
IncomingPaymentRoutes,
41+
CreateBody as IncomingCreateBody
42+
} from './open_payments/payment/incoming/routes'
4043
import { PaymentPointerKeyRoutes } from './open_payments/payment_pointer/key/routes'
4144
import { PaymentPointerRoutes } from './open_payments/payment_pointer/routes'
4245
import { IncomingPaymentService } from './open_payments/payment/incoming/service'
4346
import { StreamServer } from '@interledger/stream-receiver'
4447
import { WebhookService } from './webhook/service'
45-
import { QuoteRoutes } from './open_payments/quote/routes'
48+
import {
49+
QuoteRoutes,
50+
CreateBody as QuoteCreateBody
51+
} from './open_payments/quote/routes'
4652
import { QuoteService } from './open_payments/quote/service'
47-
import { OutgoingPaymentRoutes } from './open_payments/payment/outgoing/routes'
53+
import {
54+
OutgoingPaymentRoutes,
55+
CreateBody as OutgoingCreateBody
56+
} from './open_payments/payment/outgoing/routes'
4857
import { OutgoingPaymentService } from './open_payments/payment/outgoing/service'
4958
import { IlpPlugin, IlpPluginOptions } from './shared/ilp_plugin'
50-
import {
51-
createValidatorMiddleware,
52-
HttpMethod,
53-
isHttpMethod
54-
} from '@interledger/openapi'
59+
import { createValidatorMiddleware, HttpMethod } from '@interledger/openapi'
5560
import { PaymentPointerKeyService } from './open_payments/payment_pointer/key/service'
5661
import {
5762
AccessAction,
@@ -142,6 +147,11 @@ type CollectionContext<BodyT = never, QueryT = ParsedUrlQuery> = Omit<
142147
accessAction: NonNullable<PaymentPointerContext['accessAction']>
143148
}
144149

150+
type SignedCollectionContext<
151+
BodyT = never,
152+
QueryT = ParsedUrlQuery
153+
> = CollectionContext<BodyT, QueryT> & HttpSigContext
154+
145155
type SubresourceRequest = Omit<AppContext['request'], 'params'> & {
146156
params: Record<'id', string>
147157
}
@@ -155,6 +165,8 @@ type SubresourceContext = Omit<
155165
accessAction: NonNullable<PaymentPointerContext['accessAction']>
156166
}
157167

168+
type SignedSubresourceContext = SubresourceContext & HttpSigContext
169+
158170
export type CreateContext<BodyT> = CollectionContext<BodyT>
159171
export type ReadContext = SubresourceContext
160172
export type CompleteContext = SubresourceContext
@@ -343,109 +355,198 @@ export class App {
343355
const quoteRoutes = await this.container.use('quoteRoutes')
344356
const connectionRoutes = await this.container.use('connectionRoutes')
345357
const { resourceServerSpec } = await this.container.use('openApi')
346-
const toRouterPath = (path: string): string =>
347-
path.replace(/{/g, ':').replace(/}/g, '')
348-
349-
const toAction = ({
350-
path,
351-
method
352-
}: {
353-
path: string
354-
method: HttpMethod
355-
}): RequestAction | undefined => {
356-
switch (method) {
357-
case HttpMethod.GET:
358-
return path.endsWith('{id}') ? RequestAction.Read : RequestAction.List
359-
case HttpMethod.POST:
360-
return path.endsWith('/complete')
361-
? RequestAction.Complete
362-
: RequestAction.Create
363-
default:
364-
return undefined
365-
}
366-
}
367358

368-
const actionToRoute: {
369-
[key in RequestAction]: string
370-
} = {
371-
create: 'create',
372-
read: 'get',
373-
complete: 'complete',
374-
list: 'list'
375-
}
359+
// GET /connections/{id}
360+
router.get<DefaultState, SPSPConnectionContext>(
361+
PAYMENT_POINTER_PATH + '/connections/:id',
362+
connectionMiddleware,
363+
spspMiddleware,
364+
createValidatorMiddleware<ContextType<SPSPConnectionContext>>(
365+
resourceServerSpec,
366+
{
367+
path: '/connections/{id}',
368+
method: HttpMethod.GET
369+
}
370+
),
371+
connectionRoutes.get
372+
)
376373

377-
for (const path in resourceServerSpec.paths) {
378-
for (const method in resourceServerSpec.paths[path]) {
379-
if (isHttpMethod(method)) {
380-
const requestAction = toAction({ path, method })
381-
if (!requestAction) {
382-
throw new Error()
383-
}
374+
// POST /incoming-payments
375+
// Create incoming payment
376+
router.post<DefaultState, SignedCollectionContext<IncomingCreateBody>>(
377+
PAYMENT_POINTER_PATH + '/incoming-payments',
378+
createPaymentPointerMiddleware(),
379+
createValidatorMiddleware<
380+
ContextType<SignedCollectionContext<IncomingCreateBody>>
381+
>(resourceServerSpec, {
382+
path: '/incoming-payments',
383+
method: HttpMethod.POST
384+
}),
385+
createTokenIntrospectionMiddleware({
386+
requestType: AccessType.IncomingPayment,
387+
requestAction: RequestAction.Create
388+
}),
389+
httpsigMiddleware,
390+
incomingPaymentRoutes.create
391+
)
384392

385-
let requestType: AccessType
386-
let route: (ctx: AppContext) => Promise<void>
387-
if (path.includes('incoming-payments')) {
388-
requestType = AccessType.IncomingPayment
389-
// Use of ts-ignore will be obsolete once we get rid of this for-loop, see https://github.com/interledger/rafiki/issues/916
390-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
391-
// @ts-ignore
392-
route = incomingPaymentRoutes[actionToRoute[requestAction]]
393-
} else if (path.includes('outgoing-payments')) {
394-
requestType = AccessType.OutgoingPayment
395-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
396-
// @ts-ignore
397-
route = outgoingPaymentRoutes[actionToRoute[requestAction]]
398-
} else if (path.includes('quotes')) {
399-
requestType = AccessType.Quote
400-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
401-
// @ts-ignore
402-
route = quoteRoutes[actionToRoute[requestAction]]
403-
} else {
404-
if (path.includes('connections')) {
405-
route = connectionRoutes.get
406-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
407-
// @ts-ignore
408-
router[method](
409-
toRouterPath(path),
410-
connectionMiddleware,
411-
spspMiddleware,
412-
createValidatorMiddleware<ContextType<typeof route>>(
413-
resourceServerSpec,
414-
{
415-
path,
416-
method
417-
}
418-
),
419-
route
420-
)
421-
} else if (path !== '/' || method !== HttpMethod.GET) {
422-
// The payment pointer query route is added last below
423-
this.logger.warn({ path, method }, 'unexpected path/method')
424-
}
425-
continue
426-
}
427-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
428-
// @ts-ignore
429-
router[method](
430-
PAYMENT_POINTER_PATH + toRouterPath(path),
431-
createPaymentPointerMiddleware(),
432-
createValidatorMiddleware<ContextType<typeof route>>(
433-
resourceServerSpec,
434-
{
435-
path,
436-
method
437-
}
438-
),
439-
createTokenIntrospectionMiddleware({
440-
requestType,
441-
requestAction
442-
}),
443-
httpsigMiddleware,
444-
route
445-
)
393+
// GET /incoming-payments
394+
// List incoming payments
395+
router.get<DefaultState, SignedCollectionContext>(
396+
PAYMENT_POINTER_PATH + '/incoming-payments',
397+
createPaymentPointerMiddleware(),
398+
createValidatorMiddleware<ContextType<SignedCollectionContext>>(
399+
resourceServerSpec,
400+
{
401+
path: '/incoming-payments',
402+
method: HttpMethod.GET
446403
}
447-
}
448-
}
404+
),
405+
createTokenIntrospectionMiddleware({
406+
requestType: AccessType.IncomingPayment,
407+
requestAction: RequestAction.List
408+
}),
409+
httpsigMiddleware,
410+
incomingPaymentRoutes.list
411+
)
412+
413+
// POST /outgoing-payment
414+
// Create outgoing payment
415+
router.post<DefaultState, SignedCollectionContext<OutgoingCreateBody>>(
416+
PAYMENT_POINTER_PATH + '/outgoing-payments',
417+
createPaymentPointerMiddleware(),
418+
createValidatorMiddleware<
419+
ContextType<SignedCollectionContext<OutgoingCreateBody>>
420+
>(resourceServerSpec, {
421+
path: '/outgoing-payments',
422+
method: HttpMethod.POST
423+
}),
424+
createTokenIntrospectionMiddleware({
425+
requestType: AccessType.OutgoingPayment,
426+
requestAction: RequestAction.Create
427+
}),
428+
httpsigMiddleware,
429+
outgoingPaymentRoutes.create
430+
)
431+
432+
// GET /outgoing-payment
433+
// List outgoing payments
434+
router.get<DefaultState, SignedCollectionContext>(
435+
PAYMENT_POINTER_PATH + '/outgoing-payments',
436+
createPaymentPointerMiddleware(),
437+
createValidatorMiddleware<ContextType<SignedCollectionContext>>(
438+
resourceServerSpec,
439+
{
440+
path: '/outgoing-payments',
441+
method: HttpMethod.GET
442+
}
443+
),
444+
createTokenIntrospectionMiddleware({
445+
requestType: AccessType.OutgoingPayment,
446+
requestAction: RequestAction.List
447+
}),
448+
httpsigMiddleware,
449+
outgoingPaymentRoutes.list
450+
)
451+
452+
// POST /quotes
453+
// Create quote
454+
router.post<DefaultState, SignedCollectionContext<QuoteCreateBody>>(
455+
PAYMENT_POINTER_PATH + '/quotes',
456+
createPaymentPointerMiddleware(),
457+
createValidatorMiddleware<
458+
ContextType<SignedCollectionContext<QuoteCreateBody>>
459+
>(resourceServerSpec, {
460+
path: '/quotes',
461+
method: HttpMethod.POST
462+
}),
463+
createTokenIntrospectionMiddleware({
464+
requestType: AccessType.Quote,
465+
requestAction: RequestAction.Create
466+
}),
467+
httpsigMiddleware,
468+
quoteRoutes.create
469+
)
470+
471+
// GET /incoming-payments/{id}
472+
// Read incoming payment
473+
router.get<DefaultState, SignedSubresourceContext>(
474+
PAYMENT_POINTER_PATH + '/incoming-payments/:id',
475+
createPaymentPointerMiddleware(),
476+
createValidatorMiddleware<ContextType<SignedSubresourceContext>>(
477+
resourceServerSpec,
478+
{
479+
path: '/incoming-payments/{id}',
480+
method: HttpMethod.GET
481+
}
482+
),
483+
createTokenIntrospectionMiddleware({
484+
requestType: AccessType.IncomingPayment,
485+
requestAction: RequestAction.Read
486+
}),
487+
httpsigMiddleware,
488+
incomingPaymentRoutes.get
489+
)
490+
491+
// POST /incoming-payments/{id}/complete
492+
// Complete incoming payment
493+
router.post<DefaultState, SignedSubresourceContext>(
494+
PAYMENT_POINTER_PATH + '/incoming-payments/:id/complete',
495+
createPaymentPointerMiddleware(),
496+
createValidatorMiddleware<ContextType<SignedSubresourceContext>>(
497+
resourceServerSpec,
498+
{
499+
path: '/incoming-payments/{id}/complete',
500+
method: HttpMethod.POST
501+
}
502+
),
503+
createTokenIntrospectionMiddleware({
504+
requestType: AccessType.IncomingPayment,
505+
requestAction: RequestAction.Complete
506+
}),
507+
httpsigMiddleware,
508+
incomingPaymentRoutes.complete
509+
)
510+
511+
// GET /outgoing-payments/{id}
512+
// Read outgoing payment
513+
router.get<DefaultState, SignedSubresourceContext>(
514+
PAYMENT_POINTER_PATH + '/outgoing-payments/:id',
515+
createPaymentPointerMiddleware(),
516+
createValidatorMiddleware<ContextType<SignedSubresourceContext>>(
517+
resourceServerSpec,
518+
{
519+
path: '/outgoing-payments/{id}',
520+
method: HttpMethod.GET
521+
}
522+
),
523+
createTokenIntrospectionMiddleware({
524+
requestType: AccessType.OutgoingPayment,
525+
requestAction: RequestAction.Read
526+
}),
527+
httpsigMiddleware,
528+
outgoingPaymentRoutes.get
529+
)
530+
531+
// GET /quotes/{id}
532+
// Read quote
533+
router.get<DefaultState, SignedSubresourceContext>(
534+
PAYMENT_POINTER_PATH + '/quotes/:id',
535+
createPaymentPointerMiddleware(),
536+
createValidatorMiddleware<ContextType<SignedSubresourceContext>>(
537+
resourceServerSpec,
538+
{
539+
path: '/quotes/{id}',
540+
method: HttpMethod.GET
541+
}
542+
),
543+
createTokenIntrospectionMiddleware({
544+
requestType: AccessType.Quote,
545+
requestAction: RequestAction.Read
546+
}),
547+
httpsigMiddleware,
548+
quoteRoutes.get
549+
)
449550

450551
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
451552
// @ts-ignore

0 commit comments

Comments
 (0)