Skip to content

Commit

Permalink
feat: match on http methods (#458)
Browse files Browse the repository at this point in the history
* feat: add field to schema

* feat: write `method` into manifest

* fix: don't allow HEAD

* Update node/manifest.ts

Co-authored-by: Eduardo Bouças <mail@eduardoboucas.com>

* fix: typecheck method

* fix: types

* fix: only include defined method fields

* fix: test

---------

Co-authored-by: Eduardo Bouças <mail@eduardoboucas.com>
  • Loading branch information
Skn0tt and eduardoboucas committed Aug 25, 2023
1 parent 3340ac6 commit 72e8453
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 2 deletions.
4 changes: 4 additions & 0 deletions node/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ test('Loads function paths from the in-source `config` function', async () => {
{
function: 'user-func2',
path: '/user-func2',
method: ['PATCH'],
},
]
const result = await bundle([internalDirectory, userDirectory], distPath, declarations, {
Expand Down Expand Up @@ -207,6 +208,7 @@ test('Loads function paths from the in-source `config` function', async () => {
pattern: '^/user-func2/?$',
excluded_patterns: [],
path: '/user-func2',
methods: ['PATCH'],
})
expect(routes[2]).toEqual({
function: 'framework-func1',
Expand All @@ -231,6 +233,7 @@ test('Loads function paths from the in-source `config` function', async () => {
pattern: '^/user-func5(?:/(.*))/?$',
excluded_patterns: [],
path: '/user-func5/*',
methods: ['GET'],
})

expect(postCacheRoutes.length).toBe(1)
Expand All @@ -239,6 +242,7 @@ test('Loads function paths from the in-source `config` function', async () => {
pattern: '^/user-func4/?$',
excluded_patterns: [],
path: '/user-func4',
methods: ['POST', 'PUT'],
})

expect(Object.keys(functionConfig)).toHaveLength(1)
Expand Down
3 changes: 3 additions & 0 deletions node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const enum Cache {
Manual = 'manual',
}

export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'

export type Path = `/${string}`

export type OnError = 'fail' | 'bypass' | Path
Expand All @@ -43,6 +45,7 @@ export interface FunctionConfig {
onError?: OnError
name?: string
generator?: string
method?: HTTPMethod | HTTPMethod[]
}

const getConfigExtractor = () => {
Expand Down
8 changes: 6 additions & 2 deletions node/declaration.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import regexpAST from 'regexp-tree'

import { FunctionConfig, Path } from './config.js'
import { FunctionConfig, HTTPMethod, Path } from './config.js'
import { FeatureFlags } from './feature_flags.js'

interface BaseDeclaration {
cache?: string
function: string
method?: HTTPMethod | HTTPMethod[]
// todo: remove these two after a while and only support in-source config for non-route related configs
name?: string
generator?: string
Expand Down Expand Up @@ -93,7 +94,7 @@ const createDeclarationsFromFunctionConfigs = (
const declarations: Declaration[] = []

for (const name in functionConfigs) {
const { cache, path } = functionConfigs[name]
const { cache, path, method } = functionConfigs[name]

// If we have a path specified, create a declaration for each path.
if (!functionsVisited.has(name) && path) {
Expand All @@ -104,6 +105,9 @@ const createDeclarationsFromFunctionConfigs = (
if (cache) {
declaration.cache = cache
}
if (method) {
declaration.method = method
}
declarations.push(declaration)
})
}
Expand Down
22 changes: 22 additions & 0 deletions node/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface Route {
pattern: string
excluded_patterns: string[]
path?: string
methods?: string[]
}

interface EdgeFunctionConfig {
Expand Down Expand Up @@ -87,6 +88,23 @@ const addExcludedPatterns = (
}
}

/**
* Normalizes method names into arrays of uppercase strings.
* (e.g. "get" becomes ["GET"])
*/
const normalizeMethods = (method: unknown, name: string): string[] | undefined => {
const methods = Array.isArray(method) ? method : [method]
return methods.map((method) => {
if (typeof method !== 'string') {
throw new TypeError(
`Could not parse method declaration of function '${name}'. Expecting HTTP Method, got ${method}`,
)
}

return method.toUpperCase()
})
}

const generateManifest = ({
bundles = [],
declarations = [],
Expand Down Expand Up @@ -139,6 +157,10 @@ const generateManifest = ({
excluded_patterns: excludedPattern.map(serializePattern),
}

if ('method' in declaration) {
route.methods = normalizeMethods(declaration.method, func.name)
}

if ('path' in declaration) {
route.path = declaration.path
}
Expand Down
4 changes: 4 additions & 0 deletions node/validation/manifest/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const routesSchema = {
excluded_patterns: excludedPatternsSchema,
generator: { type: 'string' },
path: { type: 'string' },
methods: {
type: 'array',
items: { type: 'string', enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'] },
},
},
additionalProperties: false,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export default async () =>
export const config = {
cache: 'manual',
path: '/user-func4',
method: ['POST', 'PUT'],
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export default async () => new Response('Hello from user function 5.')
export const config = {
path: '/user-func5/*',
excludedPath: '/user-func5/excluded',
method: 'get',
}

0 comments on commit 72e8453

Please sign in to comment.