diff --git a/src/commands/graph/graph-init.js b/src/commands/graph/graph-init.js index 9344189dd7d..b7e25992a6a 100644 --- a/src/commands/graph/graph-init.js +++ b/src/commands/graph/graph-init.js @@ -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 @@ -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) { @@ -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( @@ -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, @@ -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, @@ -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}`) } /** diff --git a/tests/integration/220.command.graph.test.js b/tests/integration/220.command.graph.test.js index d05ee3d065f..13a6f130b82 100644 --- a/tests/integration/220.command.graph.test.js +++ b/tests/integration/220.command.graph.test.js @@ -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)) @@ -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`)) + }) + }) +})