Skip to content

Commit

Permalink
chore: support envelope in graph:init (#4947)
Browse files Browse the repository at this point in the history
* chore: support envelope env vars in graph:init

* test: adding integration tests

* test: a few more checks

* chore: adding builds scope to NETLIFY_GRAPH_PERSIST_QUERY_TOKEN env var

* chore: update contributors field

Co-authored-by: jasonbarry <jasonbarry@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 15, 2022
1 parent f6c3d78 commit 85e652e
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 38 deletions.
88 changes: 50 additions & 38 deletions src/commands/graph/graph-init.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// @ts-check
const { Buffer } = require('buffer')
const process = require('process')

const { OneGraphClient } = require('netlify-onegraph-internal')
const { v4: uuidv4 } = require('uuid')

const { OneGraphCliClient, ensureCLISession } = require('../../lib/one-graph/cli-client')
const { getNetlifyGraphConfig } = require('../../lib/one-graph/cli-netlify-graph')
const { NETLIFYDEVERR, chalk, error, exit, getToken, log } = require('../../utils')
const { msg } = require('../login/login')
const { NETLIFYDEVERR, chalk, error, exit, getToken, log, translateFromEnvelopeToMongo } = require('../../utils')

const { ensureAppForSite, executeCreateApiTokenMutation } = OneGraphCliClient

Expand All @@ -18,7 +18,8 @@ const { ensureAppForSite, executeCreateApiTokenMutation } = OneGraphCliClient
* @returns
*/
const graphInit = async (options, command) => {
const { api, config, site, state } = command.netlify
const { api, config, site, siteInfo, state } = command.netlify
const accountId = siteInfo.account_slug
const siteId = site.id

if (!siteId) {
Expand All @@ -29,30 +30,11 @@ const graphInit = async (options, command) => {
)
}

let [netlifyToken, loginLocation] = await getToken()
let [netlifyToken] = await getToken()
if (!netlifyToken) {
netlifyToken = await command.authenticate()
}

let siteData = null
try {
// @ts-ignore: we need better types for our api object
siteData = await api.getSite({ siteId })
} catch (error_) {
if (netlifyToken && error_.status === 401) {
log(`Already logged in ${msg(loginLocation)}`)
log()
log(`Run ${chalk.cyanBright('netlify status')} for account details`)
log()
log(`or run ${chalk.cyanBright('netlify switch')} to switch accounts`)
log()
log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`)
log()
return exit()
}
throw error_
}

if (netlifyToken == null) {
error(
`${NETLIFYDEVERR} Error: Unable to start Netlify Graph without a login. To enable, run ${chalk.yellow(
Expand All @@ -65,21 +47,27 @@ const graphInit = async (options, command) => {
await ensureAppForSite(netlifyToken, siteId)

const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options })
await ensureCLISession({
config,
metadata: {},
netlifyToken,
site,
state,
netlifyGraphConfig,
})

if (process.env.NODE_ENV !== 'test') {
await ensureCLISession({
config,
metadata: {},
netlifyToken,
site,
state,
netlifyGraphConfig,
})
}

let envChanged = false

// Get current environment variables set in the UI
const {
build_settings: { env = {} },
} = siteData
let env = (siteInfo.build_settings && siteInfo.build_settings.env) || {}
const isUsingEnvelope = siteInfo.use_envelope
if (isUsingEnvelope) {
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
env = translateFromEnvelopeToMongo(envelopeVariables)
}

const newEnv = {
...env,
Expand Down Expand Up @@ -120,8 +108,32 @@ const graphInit = async (options, command) => {
}
}

if (envChanged) {
// Apply environment variable updates
if (!envChanged) {
log(`Graph-related environment variables already set for site ${siteInfo.name}`)
return true
}

// Apply environment variable updates

// eslint-disable-next-line unicorn/prefer-ternary
if (isUsingEnvelope) {
await api.createEnvVars({
accountId,
siteId,
body: [
!env.NETLIFY_GRAPH_WEBHOOK_SECRET && {
key: 'NETLIFY_GRAPH_WEBHOOK_SECRET',
scopes: ['functions'],
values: [{ context: 'all', value: newEnv.NETLIFY_GRAPH_WEBHOOK_SECRET }],
},
!env.NETLIFY_GRAPH_PERSIST_QUERY_TOKEN && {
key: 'NETLIFY_GRAPH_PERSIST_QUERY_TOKEN',
scopes: ['builds', 'functions'],
values: [{ context: 'all', value: newEnv.NETLIFY_GRAPH_PERSIST_QUERY_TOKEN }],
},
].filter(Boolean),
})
} else {
// @ts-ignore
await api.updateSite({
siteId,
Expand All @@ -131,9 +143,9 @@ const graphInit = async (options, command) => {
},
},
})

log(`Finished updating Graph-related environment variables for site ${siteData.name}`)
}

log(`Finished updating Graph-related environment variables for site ${siteInfo.name}`)
}

/**
Expand Down
97 changes: 97 additions & 0 deletions tests/integration/220.command.graph.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,63 @@
const test = require('ava')

const callCli = require('./utils/call-cli')
const { getCLIOptions, withMockApi } = require('./utils/mock-api')
const { withSiteBuilder } = require('./utils/site-builder')
const { normalize } = require('./utils/snapshots')

const mongoSite = {
account_slug: 'test-account',
build_settings: {
env: {
NETLIFY_GRAPH_PERSIST_QUERY_TOKEN: 'zxcv0987',
},
},
id: 'site_id',
name: 'site-name',
}
const envelopeSite = {
...mongoSite,
use_envelope: true,
}
const envelopeResponse = [
{
key: 'NETLIFY_GRAPH_WEBHOOK_SECRET',
scopes: ['functions'],
values: [{ context: 'all', value: 'abcd1234' }],
},
{
key: 'NETLIFY_GRAPH_PERSIST_QUERY_TOKEN',
scopes: ['functions'],
values: [{ context: 'all', value: 'zxcv0987' }],
},
]
const routes = (site) => [
{ path: 'sites/site_id', response: site },
{
path: 'sites/site_id',
method: 'PATCH',
response: {},
},
{
path: 'accounts',
response: [{ slug: site.account_slug }],
},
{
path: 'sites/site_id/service-instances',
response: 'uuid-string',
},
{
path: 'accounts/test-account/env',
method: 'GET',
response: [envelopeResponse[1]],
},
{
path: 'accounts/test-account/env',
method: 'POST',
response: envelopeResponse,
},
]

test('netlify graph', async (t) => {
const cliResponse = await callCli(['graph'])
t.snapshot(normalize(cliResponse))
Expand All @@ -12,3 +67,45 @@ test('netlify graph completion', async (t) => {
const cliResponse = await callCli(['graph', 'pull'])
t.snapshot(normalize(cliResponse))
})

test('netlify graph:init with env vars from mongo', async (t) => {
await withSiteBuilder('site-env', async (builder) => {
await builder.buildAsync()

await withMockApi(routes(mongoSite), async ({ apiUrl, requests }) => {
const cliResponse = await callCli(['graph:init'], getCLIOptions({ builder, apiUrl }))

const patchRequest = requests.find(
(request) => request.method === 'PATCH' && request.path === '/api/v1/sites/site_id',
)

const WEBHOOK_SECRET_LENGTH = 48
t.is(patchRequest.body.build_settings.env.NETLIFY_GRAPH_WEBHOOK_SECRET.length, WEBHOOK_SECRET_LENGTH)
t.is(patchRequest.body.build_settings.env.NETLIFY_GRAPH_PERSIST_QUERY_TOKEN, 'zxcv0987')
t.true(cliResponse.includes(`Finished updating Graph-related environment variables for site`))
})
})
})

test('netlify graph:init with env vars from envelope', async (t) => {
await withSiteBuilder('site-env', async (builder) => {
await builder.buildAsync()

await withMockApi(routes(envelopeSite), async ({ apiUrl, requests }) => {
const cliResponse = await callCli(['graph:init'], getCLIOptions({ builder, apiUrl }))

const postRequest = requests.find(
(request) => request.method === 'POST' && request.path === '/api/v1/accounts/test-account/env',
)

const WEBHOOK_SECRET_LENGTH = 48
t.is(postRequest.body.length, 1)
t.is(postRequest.body[0].key, 'NETLIFY_GRAPH_WEBHOOK_SECRET')
t.is(postRequest.body[0].scopes[0], 'functions')
t.is(postRequest.body[0].values[0].context, 'all')
t.is(postRequest.body[0].values[0].value.length, WEBHOOK_SECRET_LENGTH)

t.true(cliResponse.includes(`Finished updating Graph-related environment variables for site`))
})
})
})

1 comment on commit 85e652e

@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

Package size: 222 MB

Please sign in to comment.