Skip to content

Commit

Permalink
feat(graph): update graph helpers to work better with next.js (#354)
Browse files Browse the repository at this point in the history
* feat(graph): update graph helpers to work better with next.js

* chore(graph): update type
  • Loading branch information
dwwoelfel authored Aug 12, 2022
1 parent b51b391 commit b854652
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 39 deletions.
1 change: 1 addition & 0 deletions src/function/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ export interface Event {
multiValueQueryStringParameters: EventMultiValueQueryStringParameters | null
body: string | null
isBase64Encoded: boolean
netlifyGraphToken: string | undefined
}
10 changes: 9 additions & 1 deletion src/function/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@ export { Context as HandlerContext } from './context'
export { Event as HandlerEvent } from './event'
export { Handler, HandlerCallback } from './handler'
export { Response as HandlerResponse } from './response'
export { getSecrets, withSecrets, getNetlifyGraphToken, GraphTokenResponse, HasHeaders } from '../lib/graph'
export {
getSecrets,
getSecretsForBuild,
withSecrets,
getNetlifyGraphToken,
getNetlifyGraphTokenForBuild,
GraphTokenResponse,
HasHeaders,
} from '../lib/graph'
export { NetlifySecrets } from '../lib/secrets_helper'
4 changes: 2 additions & 2 deletions src/lib/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Response } from '../function/response'

import { getSecrets, NetlifySecrets } from './secrets_helper'
// Fine-grained control during the preview, less necessary with a more proactive OneGraph solution
export { getSecrets } from './secrets_helper'
export { getNetlifyGraphToken, GraphTokenResponse, HasHeaders } from './graph_token'
export { getSecrets, getSecretsForBuild } from './secrets_helper'
export { getNetlifyGraphToken, getNetlifyGraphTokenForBuild, GraphTokenResponse, HasHeaders } from './graph_token'

export interface ContextWithSecrets extends Context {
secrets: NetlifySecrets
Expand Down
46 changes: 30 additions & 16 deletions src/lib/graph_token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type GraphTokenResponse = {
}

const TOKEN_HEADER = 'X-Nf-Graph-Token'
const TOKEN_HEADER_NORMALIZED = 'x-nf-graph-token'

// Matches Web API Headers type (https://developer.mozilla.org/en-US/docs/Web/API/Headers)
interface RequestHeaders {
Expand All @@ -33,21 +34,37 @@ const hasRequestStyleHeaders = function (headers: RequestHeaders | IncomingHttpH
const graphTokenFromIncomingHttpStyleHeaders = function (
headers: RequestHeaders | IncomingHttpHeaders,
): string | null | undefined {
if (TOKEN_HEADER in headers) {
const header = headers[TOKEN_HEADER]
if (header == null || typeof header === 'string') {
return header
if (TOKEN_HEADER in headers || TOKEN_HEADER_NORMALIZED in headers) {
const header = headers[TOKEN_HEADER] || headers[TOKEN_HEADER_NORMALIZED]
if (Array.isArray(header)) {
return header[0]
}
return header[0]
return header
}
}

// Backwards compatibility with older version of cli that doesn't inject header
const authlifyTokenFallback = function (event: HasHeaders): GraphTokenResponse {
const token = (event as { authlifyToken?: string | null })?.authlifyToken
const graphTokenFromEnv = function (): GraphTokenResponse {
// _NETLIFY_GRAPH_TOKEN injected by next plugin
// eslint-disable-next-line no-underscore-dangle
const token = env._NETLIFY_GRAPH_TOKEN || env.NETLIFY_GRAPH_TOKEN
return { token }
}

const tokenFallback = function (event: HasHeaders): GraphTokenResponse {
// Backwards compatibility with older version of cli that doesn't inject header
const token = (event as { authlifyToken?: string | null })?.authlifyToken
if (token) {
return { token }
}

// If we're in dev-mode with next.js, the plugin won't be there to inject
// secrets, so we need to get the token from the environment
if (env.NETLIFY_DEV === 'true') {
return graphTokenFromEnv()
}
return { token: null }
}

const graphTokenFromEvent = function (event: HasHeaders): GraphTokenResponse {
const { headers } = event
// Check if object first in case there is a header with key `get`
Expand All @@ -60,14 +77,7 @@ const graphTokenFromEvent = function (event: HasHeaders): GraphTokenResponse {
return { token: headers.get(TOKEN_HEADER) }
}

return authlifyTokenFallback(event)
}

const graphTokenFromEnv = function (): GraphTokenResponse {
// _NETLIFY_GRAPH_TOKEN injected by next plugin
// eslint-disable-next-line no-underscore-dangle
const token = env._NETLIFY_GRAPH_TOKEN || env.NETLIFY_GRAPH_TOKEN
return { token }
return tokenFallback(event)
}

const isEventRequired = function (): boolean {
Expand Down Expand Up @@ -125,3 +135,7 @@ export const getNetlifyGraphToken = function (

return event ? graphTokenFromEvent(event) : graphTokenFromEnv()
}

export const getNetlifyGraphTokenForBuild = function (): GraphTokenResponse {
return graphTokenFromEnv()
}
55 changes: 35 additions & 20 deletions src/lib/secrets_helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { graphRequest } from './graph_request'
import { getNetlifyGraphToken, GraphTokenResponseError, HasHeaders } from './graph_token'
import { getNetlifyGraphToken, getNetlifyGraphTokenForBuild, GraphTokenResponseError, HasHeaders } from './graph_token'

const services = {
gitHub: null,
Expand Down Expand Up @@ -102,23 +102,11 @@ const logErrors = function (errors: GraphTokenResponseError[]) {
}
}

// Note: We may want to have configurable "sets" of secrets,
// e.g. "dev" and "prod"
export const getSecrets = async (event?: HasHeaders | null | undefined): Promise<NetlifySecrets> => {
const graphTokenResponse = getNetlifyGraphToken(event, true)
const graphToken = graphTokenResponse.token
if (!graphToken) {
if (graphTokenResponse.errors) {
logErrors(graphTokenResponse.errors)
}
return {}
}

// We select for more than we typeically need here
// in order to allow for some metaprogramming for
// consumers downstream. Also, the data is typically
// static and shouldn't add any measurable overhead.
const doc = `query FindLoggedInServicesQuery {
// We select for more than we typically need here
// in order to allow for some metaprogramming for
// consumers downstream. Also, the data is typically
// static and shouldn't add any measurable overhead.
const findLoggedInServicesQuery = `query FindLoggedInServicesQuery {
me {
serviceMetadata {
loggedInServices {
Expand All @@ -143,13 +131,40 @@ export const getSecrets = async (event?: HasHeaders | null | undefined): Promise
}
}`

const body = JSON.stringify({ query: doc })
const getSecretsForToken = async (token: string): Promise<NetlifySecrets> => {
const body = JSON.stringify({ query: findLoggedInServicesQuery })

// eslint-disable-next-line node/no-unsupported-features/node-builtins
const resultBody = await graphRequest(graphToken, new TextEncoder().encode(body))
const resultBody = await graphRequest(token, new TextEncoder().encode(body))
const result: GraphSecretsResponse = JSON.parse(resultBody)

const newSecrets = formatSecrets(result)

return newSecrets
}

export const getSecrets = async (event?: HasHeaders | null | undefined): Promise<NetlifySecrets> => {
const graphTokenResponse = getNetlifyGraphToken(event, true)
const graphToken = graphTokenResponse.token
if (!graphToken) {
if (graphTokenResponse.errors) {
logErrors(graphTokenResponse.errors)
}
return {}
}

return await getSecretsForToken(graphToken)
}

export const getSecretsForBuild = async (): Promise<NetlifySecrets> => {
const graphTokenResponse = getNetlifyGraphTokenForBuild()
const graphToken = graphTokenResponse.token
if (!graphToken) {
if (graphTokenResponse.errors) {
logErrors(graphTokenResponse.errors)
}
return {}
}

return await getSecretsForToken(graphToken)
}

0 comments on commit b854652

Please sign in to comment.