Skip to content

Commit

Permalink
feat: set Netlify environment variables in edge functions (netlify#6229)
Browse files Browse the repository at this point in the history
* feat: set Netlify environment variables in edge functions

* chore: fix test

* refactor: use `localhost`

* fix: use right protocol
  • Loading branch information
eduardoboucas authored Nov 30, 2023
1 parent d37ec4b commit 3268f29
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/lib/edge-functions/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env } from 'process'

const latestBootstrapURL = 'https://65437779a0c9990008b54abe--edge.netlify.com/bootstrap/index-combined.ts'
const latestBootstrapURL = 'https://656703bb61f20c00084a3479--edge.netlify.com/bootstrap/index-combined.ts'

export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
21 changes: 15 additions & 6 deletions src/lib/edge-functions/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,18 @@ export const handleProxyRequest = (req, proxyReq) => {
})
}

export const createSiteInfoHeader = (siteInfo = {}) => {
// @ts-expect-error TS(2339) FIXME: Property 'id' does not exist on type '{}'.
// TODO: This should be replaced with a proper type for the entire API response
// for the site endpoint.
// See https://github.com/netlify/build/pull/5308.
interface SiteInfo {
id: string
name: string
url: string
}

export const createSiteInfoHeader = (siteInfo: SiteInfo, localURL: string) => {
const { id, name, url } = siteInfo
const site = { id, name, url }
const site = { id, name, url: localURL ?? url }
const siteString = JSON.stringify(site)
return Buffer.from(siteString).toString('base64')
}
Expand Down Expand Up @@ -137,7 +145,8 @@ export const initializeProxy = async ({
const buildFeatureFlags = {
edge_functions_npm_modules: true,
}
const runtimeFeatureFlags = ['edge_functions_bootstrap_failure_mode']
const runtimeFeatureFlags = ['edge_functions_bootstrap_failure_mode', 'edge_functions_bootstrap_populate_environment']
const protocol = settings.https ? 'https' : 'http'

// Initializes the server, bootstrapping the Deno CLI and downloading it from
// the network if needed. We don't want to wait for that to be completed, or
Expand Down Expand Up @@ -176,7 +185,7 @@ export const initializeProxy = async ({
// Setting header with geolocation and site info.
req.headers[headers.Geo] = Buffer.from(JSON.stringify(geoLocation)).toString('base64')
req.headers[headers.DeployID] = '0'
req.headers[headers.Site] = createSiteInfoHeader(siteInfo)
req.headers[headers.Site] = createSiteInfoHeader(siteInfo, `${protocol}://localhost:${mainPort}`)
req.headers[headers.Account] = createAccountInfoHeader({ id: accountId })

if (blobsContext?.edgeURL && blobsContext?.token) {
Expand All @@ -196,7 +205,7 @@ export const initializeProxy = async ({

req[headersSymbol] = {
[headers.FeatureFlags]: getFeatureFlagsHeader(runtimeFeatureFlags),
[headers.ForwardedProtocol]: settings.https ? 'https:' : 'http:',
[headers.ForwardedProtocol]: `${protocol}:`,
[headers.Functions]: functionNames.join(','),
[headers.InvocationMetadata]: getInvocationMetadataHeader(invocationMetadata),
[headers.IP]: LOCAL_HOST,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Config, Context } from 'https://edge.netlify.com'

export default (_, context: Context) => Response.json(Netlify.env.toObject())

export const config: Config = {
path: '/echo-env',
}
7 changes: 5 additions & 2 deletions tests/integration/commands/dev/dev-miscellaneous.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ describe.concurrent('commands/dev-miscellaneous', () => {
t.expect(response.status).toBe(200)
t.expect(JSON.parse(await response.text())).toStrictEqual({
deploy: { id: '0' },
site: { id: 'site_id', name: 'site-name', url: 'site-url' },
site: { id: 'site_id', name: 'site-name', url: server.url },
})
},
)
Expand Down Expand Up @@ -1110,7 +1110,10 @@ describe.concurrent('commands/dev-miscellaneous', () => {
t.expect(bucketKeys.includes('DENO_DEPLOYMENT_ID')).toBe(false)
t.expect(bucketKeys.includes('NODE_ENV')).toBe(false)
t.expect(bucketKeys.includes('DEPLOY_URL')).toBe(false)
t.expect(bucketKeys.includes('URL')).toBe(false)

t.expect(bucketKeys.includes('URL')).toBe(true)
t.expect(bucketKeys.includes('SITE_ID')).toBe(true)
t.expect(bucketKeys.includes('SITE_NAME')).toBe(true)
})
},
)
Expand Down
15 changes: 14 additions & 1 deletion tests/integration/commands/dev/edge-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe.skipIf(isWindows)('edge functions', () => {
expect(params).toEqual({})
expectTypeOf(requestId).toBeString()
expect(server).toEqual({ region: 'local' })
expect(site).toEqual({ id: 'foo', name: 'site-name' })
expect(site).toEqual({ id: 'foo', name: 'site-name', url: `http://localhost:${devServer.port}` })
})

test<FixtureTestContext>('should expose URL parameters', async ({ devServer }) => {
Expand Down Expand Up @@ -135,6 +135,19 @@ 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 set the `URL`, `SITE_ID`, and `SITE_NAME` environment variables', async ({
devServer,
}) => {
const body = (await got(`http://localhost:${devServer.port}/echo-env`, {
throwHttpErrors: false,
retry: { limit: 0 },
}).json()) as Record<string, string>

expect(body.SITE_ID).toBe('foo')
expect(body.SITE_NAME).toBe('site-name')
expect(body.URL).toBe(`http://localhost:${devServer.port}`)
})
})

setupFixtureTests('dev-server-with-edge-functions', { devServer: true, mockApi: { routes } }, () => {
Expand Down

0 comments on commit 3268f29

Please sign in to comment.