Skip to content

Commit

Permalink
feat: add Blobs context to edge functions (#6109)
Browse files Browse the repository at this point in the history
* feat: simplify Blobs context

* feat: add Blobs context to edge functions

* chore: remove comment
  • Loading branch information
eduardoboucas authored Oct 31, 2023
1 parent 786b3c5 commit c770ba0
Show file tree
Hide file tree
Showing 19 changed files with 104 additions and 49 deletions.
1 change: 1 addition & 0 deletions src/commands/dev/dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ const dev = async (options, command) => {

await startProxyServer({
addonsUrls,
blobsContext,
config,
configPath: configPathOverride,
debug: options.debug,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/blobs/blobs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { getPathInProject } from '../settings.mjs'
* @param {boolean} options.debug
* @param {string} options.projectRoot
* @param {string} options.siteID
* @returns {BlobsContext}
* @returns {Promise<BlobsContext>}
*/
export const getBlobsContext = async ({ debug, projectRoot, siteID }) => {
const token = uuidv4()
Expand Down
2 changes: 1 addition & 1 deletion src/lib/edge-functions/bootstrap.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env } from 'process'

const latestBootstrapURL = 'https://650bfd807b21ed000893e25c--edge.netlify.com/bootstrap/index-combined.ts'
const latestBootstrapURL = 'https://6539213a19a93a000876a033--edge.netlify.com/bootstrap/index-combined.ts'

export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
1 change: 1 addition & 0 deletions src/lib/edge-functions/headers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Buffer } from 'buffer'

export const headers = {
BlobsInfo: 'x-nf-blobs-info',
DeployID: 'x-nf-deploy-id',
FeatureFlags: 'x-nf-feature-flags',
ForwardedHost: 'x-forwarded-host',
Expand Down
8 changes: 8 additions & 0 deletions src/lib/edge-functions/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const createAccountInfoHeader = (accountInfo = {}) => {
*
* @param {object} config
* @param {*} config.accountId
* @param {import("../blobs/blobs.mjs").BlobsContext} config.blobsContext
* @param {*} config.config
* @param {*} config.configPath
* @param {*} config.debug
Expand All @@ -85,6 +86,7 @@ export const createAccountInfoHeader = (accountInfo = {}) => {
*/
export const initializeProxy = async ({
accountId,
blobsContext,
config,
configPath,
debug,
Expand Down Expand Up @@ -151,6 +153,12 @@ export const initializeProxy = async ({
req.headers[headers.Site] = createSiteInfoHeader(siteInfo)
req.headers[headers.Account] = createAccountInfoHeader({ id: accountId })

if (blobsContext?.edgeURL && blobsContext?.token) {
req.headers[headers.BlobsInfo] = Buffer.from(
JSON.stringify({ url: blobsContext.edgeURL, token: blobsContext.token }),
).toString('base64')
}

await registry.initialize()

const url = new URL(req.url, `http://${LOCAL_HOST}:${mainPort}`)
Expand Down
24 changes: 8 additions & 16 deletions src/lib/functions/netlify-function.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -195,22 +195,14 @@ export default class NetlifyFunction {
const environment = {}

if (this.blobsContext) {
if (this.runtimeAPIVersion === 2) {
// For functions using the v2 API, we inject the context object into an
// environment variable.
environment.NETLIFY_BLOBS_CONTEXT = Buffer.from(JSON.stringify(this.blobsContext)).toString('base64')
} else {
const payload = JSON.stringify({
url: this.blobsContext.edgeURL,
token: this.blobsContext.token,
})

// For functions using the Lambda compatibility mode, we pass the
// context as part of the `clientContext` property.
context.custom = {
...context?.custom,
blobs: Buffer.from(payload).toString('base64'),
}
const payload = JSON.stringify({
url: this.blobsContext.edgeURL,
token: this.blobsContext.token,
})

context.custom = {
...context?.custom,
blobs: Buffer.from(payload).toString('base64'),
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/functions/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const createHandler = function (options) {
'client-ip': [remoteAddress],
'x-nf-client-connection-ip': [remoteAddress],
'x-nf-account-id': [options.accountId],
'x-nf-site-id': [options?.siteInfo?.id],
[efHeaders.Geo]: Buffer.from(JSON.stringify(geoLocation)).toString('base64'),
}).reduce((prev, [key, value]) => ({ ...prev, [key]: Array.isArray(value) ? value : [value] }), {})
const rawQuery = new URLSearchParams(requestQuery).toString()
Expand Down
3 changes: 3 additions & 0 deletions src/utils/proxy-server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
* @param {object} params
* @param {string=} params.accountId
* @param {*} params.addonsUrls
* @param {import("../lib/blobs/blobs.mjs").BlobsContext} blobsContext
* @param {import('../commands/types.js').NetlifyOptions["config"]} params.config
* @param {string} [params.configPath] An override for the Netlify config path
* @param {boolean} params.debug
Expand All @@ -58,6 +59,7 @@ export const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
export const startProxyServer = async ({
accountId,
addonsUrls,
blobsContext,
config,
configPath,
debug,
Expand All @@ -76,6 +78,7 @@ export const startProxyServer = async ({
}) => {
const url = await startProxy({
addonsUrls,
blobsContext,
config,
configPath: configPath || site.configPath,
debug,
Expand Down
2 changes: 2 additions & 0 deletions src/utils/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ export const getProxyUrl = function (settings) {
export const startProxy = async function ({
accountId,
addonsUrls,
blobsContext,
config,
configPath,
debug,
Expand All @@ -693,6 +694,7 @@ export const startProxy = async function ({
const secondaryServerPort = settings.https ? await getAvailablePort() : null
const functionsServer = settings.functionsPort ? `http://127.0.0.1:${settings.functionsPort}` : null
const edgeFunctionsProxy = await initializeEdgeFunctionsProxy({
blobsContext,
config,
configPath,
debug,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
publish = "public"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getStore } from '@netlify/blobs'

export default async () => {
const store = getStore('my-store')
const metadata = {
name: 'Netlify',
features: {
blobs: true,
functions: true,
},
}

await store.set('my-key', 'hello world', { metadata })

const entry = await store.getWithMetadata('my-key')

return Response.json(entry)
}

export const config = {
path: '/blobs',
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"@netlify/blobs": "^4.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
origin

This file was deleted.

This file was deleted.

This file was deleted.

37 changes: 26 additions & 11 deletions tests/integration/commands/dev/edge-functions.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import process from 'process'

import execa from 'execa'
import { describe, expect, expectTypeOf, test } from 'vitest'

import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js'
Expand Down Expand Up @@ -30,6 +31,10 @@ const routes = [
},
]

const setup = async ({ fixture }) => {
await execa('npm', ['install'], { cwd: fixture.directory })
}

describe.skipIf(isWindows)('edge functions', () => {
setupFixtureTests('dev-server-with-edge-functions', { devServer: true, mockApi: { routes } }, () => {
test<FixtureTestContext>('should run edge functions in correct order', async ({ devServer }) => {
Expand Down Expand Up @@ -115,17 +120,6 @@ describe.skipIf(isWindows)('edge functions', () => {

expect(res2.body).toContain('<p>An unhandled error in the function code triggered the following message:</p>')
})

test<FixtureTestContext>('should run an edge function that imports an npm module', async ({ devServer }) => {
const res = await got(`http://localhost:${devServer.port}/with-npm-module`, {
method: 'GET',
throwHttpErrors: false,
retry: { limit: 0 },
})

expect(res.statusCode).toBe(200)
expect(res.body).toBe('Hello from an npm module!')
})
})

setupFixtureTests('dev-server-with-edge-functions', { devServer: true, mockApi: { routes } }, () => {
Expand All @@ -146,4 +140,25 @@ describe.skipIf(isWindows)('edge functions', () => {
expect(devServer.output).not.toContain('Removed edge function')
})
})

setupFixtureTests(
'dev-server-with-edge-functions-and-npm-modules',
{ devServer: true, mockApi: { routes }, setup },
() => {
test<FixtureTestContext>('should run an edge function that uses the Blobs npm module', async ({ devServer }) => {
const res = await got(`http://localhost:${devServer.port}/blobs`, {
method: 'GET',
throwHttpErrors: false,
retry: { limit: 0 },
})

expect(res.statusCode).toBe(200)
expect(JSON.parse(res.body)).toEqual({
data: 'hello world',
fresh: false,
metadata: { name: 'Netlify', features: { blobs: true, functions: true } },
})
})
},
)
})
4 changes: 2 additions & 2 deletions tests/integration/utils/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export async function setupFixtureTests(
if (options.mockApi) mockApi = await startMockApi(options.mockApi)
fixture = await Fixture.create(fixturePath, { apiUrl: mockApi?.apiUrl })

await options.setup?.({ fixture, mockApi })

if (options.devServer) {
devServer = await startDevServer({
cwd: fixture.directory,
Expand All @@ -156,8 +158,6 @@ export async function setupFixtureTests(
},
})
}

await options.setup?.({ devServer, fixture, mockApi })
}, HOOK_TIMEOUT)

beforeEach<FixtureTestContext>((context) => {
Expand Down

2 comments on commit c770ba0

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Dependency count: 1,373
  • Package size: 381 MB

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Dependency count: 1,373
  • Package size: 381 MB

Please sign in to comment.