diff --git a/README.md b/README.md index 9c287267361..abd9bd1132e 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ Manage netlify functions | [`graph:init`](/docs/commands/graph.md#graphinit) | Initialize all the resources for Netlify Graph | | [`graph:library`](/docs/commands/graph.md#graphlibrary) | Generate the Graph function library | | [`graph:operations`](/docs/commands/graph.md#graphoperations) | List all of the locally available operations | -| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull down your local Netlify Graph schema, and process pending Graph edit events | +| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull your remote Netlify Graph schema locally, and process pending Graph edit events | ### [init](/docs/commands/init.md) diff --git a/docs/README.md b/docs/README.md index cbcc967a26b..5ac86b2770c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -121,7 +121,7 @@ Manage netlify functions | [`graph:init`](/docs/commands/graph.md#graphinit) | Initialize all the resources for Netlify Graph | | [`graph:library`](/docs/commands/graph.md#graphlibrary) | Generate the Graph function library | | [`graph:operations`](/docs/commands/graph.md#graphoperations) | List all of the locally available operations | -| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull down your local Netlify Graph schema, and process pending Graph edit events | +| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull your remote Netlify Graph schema locally, and process pending Graph edit events | ### [init](/docs/commands/init.md) diff --git a/docs/commands/graph.md b/docs/commands/graph.md index 66042771f84..426cb565016 100644 --- a/docs/commands/graph.md +++ b/docs/commands/graph.md @@ -28,7 +28,7 @@ netlify graph | [`graph:init`](/docs/commands/graph.md#graphinit) | Initialize all the resources for Netlify Graph | | [`graph:library`](/docs/commands/graph.md#graphlibrary) | Generate the Graph function library | | [`graph:operations`](/docs/commands/graph.md#graphoperations) | List all of the locally available operations | -| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull down your local Netlify Graph schema, and process pending Graph edit events | +| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull your remote Netlify Graph schema locally, and process pending Graph edit events | **Examples** @@ -147,7 +147,7 @@ netlify graph:operations --- ## `graph:pull` -Pull down your local Netlify Graph schema, and process pending Graph edit events +Pull your remote Netlify Graph schema locally, and process pending Graph edit events **Usage** diff --git a/docs/commands/index.md b/docs/commands/index.md index c01b3fb5a9e..51ee61faf6a 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -102,7 +102,7 @@ Manage netlify functions | [`graph:init`](/docs/commands/graph.md#graphinit) | Initialize all the resources for Netlify Graph | | [`graph:library`](/docs/commands/graph.md#graphlibrary) | Generate the Graph function library | | [`graph:operations`](/docs/commands/graph.md#graphoperations) | List all of the locally available operations | -| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull down your local Netlify Graph schema, and process pending Graph edit events | +| [`graph:pull`](/docs/commands/graph.md#graphpull) | Pull your remote Netlify Graph schema locally, and process pending Graph edit events | ### [init](/docs/commands/init.md) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c41eb12e7dc..2e4285e2fbe 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -81,7 +81,7 @@ "multiparty": "^4.2.1", "netlify": "^12.0.0", "netlify-headers-parser": "^6.0.2", - "netlify-onegraph-internal": "0.4.1", + "netlify-onegraph-internal": "0.4.2", "netlify-redirect-parser": "^13.0.5", "netlify-redirector": "^0.2.1", "node-fetch": "^2.6.0", @@ -16384,12 +16384,13 @@ } }, "node_modules/netlify-onegraph-internal": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/netlify-onegraph-internal/-/netlify-onegraph-internal-0.4.1.tgz", - "integrity": "sha512-b+9gNOJHUeFZ9DoUDYuOLxMhtfIibEO6PZKSj3Xf5TtV9k536uX2c963kldb3VQ2HfIv9S5H5iirgkXcJfzXHw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/netlify-onegraph-internal/-/netlify-onegraph-internal-0.4.2.tgz", + "integrity": "sha512-3TGD/s2FGjx9NcOcMPMMUxamy3IVY9O0ZZ176FuHVDmLAc5l8mj0ZNoUbhdrXfZjes62Gt6/YynCJxGShE6oUA==", "dependencies": { "graphql": "16.0.0", "node-fetch": "^2.6.0", + "rusha": "^0.8.14", "uuid": "^8.3.2" } }, @@ -19977,6 +19978,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rusha": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz", + "integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==" + }, "node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -35257,12 +35263,13 @@ } }, "netlify-onegraph-internal": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/netlify-onegraph-internal/-/netlify-onegraph-internal-0.4.1.tgz", - "integrity": "sha512-b+9gNOJHUeFZ9DoUDYuOLxMhtfIibEO6PZKSj3Xf5TtV9k536uX2c963kldb3VQ2HfIv9S5H5iirgkXcJfzXHw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/netlify-onegraph-internal/-/netlify-onegraph-internal-0.4.2.tgz", + "integrity": "sha512-3TGD/s2FGjx9NcOcMPMMUxamy3IVY9O0ZZ176FuHVDmLAc5l8mj0ZNoUbhdrXfZjes62Gt6/YynCJxGShE6oUA==", "requires": { "graphql": "16.0.0", "node-fetch": "^2.6.0", + "rusha": "^0.8.14", "uuid": "^8.3.2" }, "dependencies": { @@ -37945,6 +37952,11 @@ "queue-microtask": "^1.2.2" } }, + "rusha": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz", + "integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==" + }, "rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", diff --git a/package.json b/package.json index d1441bd8f32..2bbd09502d9 100644 --- a/package.json +++ b/package.json @@ -291,7 +291,7 @@ "multiparty": "^4.2.1", "netlify": "^12.0.0", "netlify-headers-parser": "^6.0.2", - "netlify-onegraph-internal": "0.4.1", + "netlify-onegraph-internal": "0.4.2", "netlify-redirect-parser": "^13.0.5", "netlify-redirector": "^0.2.1", "node-fetch": "^2.6.0", diff --git a/src/commands/graph/graph-edit.js b/src/commands/graph/graph-edit.js index d360a792fe7..518a0980aa4 100644 --- a/src/commands/graph/graph-edit.js +++ b/src/commands/graph/graph-edit.js @@ -48,6 +48,7 @@ const graphEdit = async (options, command) => { netlifyToken, site, state, + netlifyGraphConfig, }) const { branch } = gitRepoInfo() diff --git a/src/commands/graph/graph-init.js b/src/commands/graph/graph-init.js index 681fee3d257..55cb21e41ed 100644 --- a/src/commands/graph/graph-init.js +++ b/src/commands/graph/graph-init.js @@ -5,6 +5,7 @@ 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') @@ -63,11 +64,13 @@ const graphInit = async (options, command) => { await ensureAppForSite(netlifyToken, siteId) + const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options }) await ensureCLISession({ metadata: {}, netlifyToken, site, state, + netlifyGraphConfig, }) let envChanged = false diff --git a/src/commands/graph/graph-library.js b/src/commands/graph/graph-library.js index 5818a318e51..c091306cb93 100644 --- a/src/commands/graph/graph-library.js +++ b/src/commands/graph/graph-library.js @@ -1,4 +1,5 @@ // @ts-check +const { readLockfile } = require('../../lib/one-graph/cli-client') const { buildSchema, defaultExampleOperationsDoc, @@ -9,7 +10,7 @@ const { readGraphQLOperationsSourceFile, readGraphQLSchemaFile, } = require('../../lib/one-graph/cli-netlify-graph') -const { error, log } = require('../../utils') +const { NETLIFYDEVERR, chalk, error, log } = require('../../utils') /** * Creates the `netlify graph:library` command @@ -42,10 +43,23 @@ const graphLibrary = async (options, command) => { const parsedDoc = parse(currentOperationsDoc) const { fragments, functions } = extractFunctionsFromOperationDoc(parsedDoc) + const lockfile = readLockfile({ siteRoot: command.netlify.site.root }) + + if (lockfile == null) { + error( + `${NETLIFYDEVERR} Error: no lockfile found, unable to run \`netlify graph:library\`. To pull a remote schema (and create a lockfile), run ${chalk.yellow( + 'netlify graph:pull', + )} `, + ) + } + + const schemaId = lockfile && lockfile.locked.schemaId + generateFunctionsFile({ logger: log, netlifyGraphConfig, schema, + schemaId, operationsDoc: currentOperationsDoc, functions, fragments, diff --git a/src/commands/graph/graph-pull.js b/src/commands/graph/graph-pull.js index 093aad56689..60923be87cc 100644 --- a/src/commands/graph/graph-pull.js +++ b/src/commands/graph/graph-pull.js @@ -34,6 +34,13 @@ const graphPull = async (options, command) => { const { jwt } = await OneGraphCliClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken }) const oneGraphSessionId = loadCLISession(state) + if (!oneGraphSessionId) { + warn( + 'No local Netlify Graph session found, skipping command queue drain. Create a new session by running `netlify graph:edit`.', + ) + return + } + await refetchAndGenerateFromOneGraph({ logger: log, netlifyGraphConfig, @@ -43,13 +50,6 @@ const graphPull = async (options, command) => { sessionId: oneGraphSessionId, }) - if (!oneGraphSessionId) { - warn( - 'No local Netlify Graph session found, skipping command queue drain. Create a new session by running `netlify graph:edit`.', - ) - return - } - const schemaString = readGraphQLSchemaFile(netlifyGraphConfig) let schema @@ -106,7 +106,7 @@ const graphPull = async (options, command) => { const createGraphPullCommand = (program) => program .command('graph:pull') - .description('Pull down your local Netlify Graph schema, and process pending Graph edit events') + .description('Pull your remote Netlify Graph schema locally, and process pending Graph edit events') .action(async (options, command) => { await graphPull(options, command) }) diff --git a/src/lib/one-graph/cli-client.js b/src/lib/one-graph/cli-client.js index ec3eeb72cb2..e1bc9171722 100644 --- a/src/lib/one-graph/cli-client.js +++ b/src/lib/one-graph/cli-client.js @@ -2,12 +2,13 @@ /* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable fp/no-loops */ const crypto = require('crypto') +const { readFileSync, writeFileSync } = require('fs') const os = require('os') const path = require('path') const gitRepoInfo = require('git-repo-info') const { GraphQL, InternalConsole, OneGraphClient } = require('netlify-onegraph-internal') -const { NetlifyGraph } = require('netlify-onegraph-internal') +const { NetlifyGraph, NetlifyGraphLockfile } = require('netlify-onegraph-internal') // eslint-disable-next-line no-unused-vars const { StateConfig, USER_AGENT, chalk, error, execa, log, warn, watchDebounced } = require('../../utils') @@ -273,6 +274,33 @@ const regenerateFunctionsFileFromOperationsFile = (input) => { generateFunctionsFile({ netlifyGraphConfig, schema, operationsDoc: appOperationsDoc, functions, fragments }) } +/** + * Lockfile Operations + */ + +/** + * Persist the Netlify Graph lockfile on disk + * @param {object} input + * @param {string} input.siteRoot The GraphQL schema to use when generating code + * @param {NetlifyGraphLockfile.V0_format} input.lockfile + */ +const writeLockfile = ({ lockfile, siteRoot }) => { + writeFileSync(path.join(siteRoot, NetlifyGraphLockfile.defaultLockFileName), JSON.stringify(lockfile, null, 2)) +} + +/** + * Read the Netlify Graph lockfile from disk, if it exists + * @param {object} input + * @param {string} input.siteRoot The GraphQL schema to use when generating code + * @return {NetlifyGraphLockfile.V0_format | undefined} + */ +const readLockfile = ({ siteRoot }) => { + try { + const buf = readFileSync(path.join(siteRoot, NetlifyGraphLockfile.defaultLockFileName)) + return JSON.parse(buf.toString('utf8')) + } catch {} +} + /** * Compute a md5 hash of a string * @param {string} input String to compute a quick md5 hash for @@ -285,7 +313,8 @@ const quickHash = (input) => { } /** - * Fetch a persisted operations doc by its id, write it to the system, and regenerate the library + * Fetch a persisted operations doc by its id, normalize it for Netlify Graph + * and return its contents as a string * @param {object} input * @param {string} input.siteId The site id to query against * @param {string} input.netlifyToken The (typically netlify) access token that is used for authentication, if any @@ -293,11 +322,11 @@ const quickHash = (input) => { * @param {(message: string) => void=} input.logger A function that if provided will be used to log messages * @param {GraphQL.GraphQLSchema} input.schema The GraphQL schema to use when generating code * @param {NetlifyGraph.NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events - * @returns + * @returns {Promise} */ -const updateGraphQLOperationsFileFromPersistedDoc = async (input) => { +const fetchGraphQLOperationsLibraryFromPersistedDoc = async (input) => { try { - const { docId, logger, netlifyGraphConfig, netlifyToken, schema, siteId } = input + const { docId, netlifyToken, siteId } = input const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken }) const persistedDoc = await OneGraphClient.fetchPersistedQuery(jwt, siteId, docId) if (!persistedDoc) { @@ -308,21 +337,63 @@ const updateGraphQLOperationsFileFromPersistedDoc = async (input) => { // Sorts the operations stably, prepends the @netlify directive, etc. const operationsDocString = normalizeOperationsDoc(persistedDoc.query) - writeGraphQLOperationsSourceFile({ logger, netlifyGraphConfig, operationsDocString }) - regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema }) + return operationsDocString + } catch { + warn(`Unable to reach Netlify Graph servers in order to update Graph operations file`) + } +} + +/** + * Fetch a persisted operations doc by its id, write it to the system, and regenerate the library + * @param {object} input + * @param {string} input.operationsDocString The contents of the GraphQL operations document + * @param {(message: string) => void=} input.logger A function that if provided will be used to log messages + * @param {GraphQL.GraphQLSchema} input.schema The GraphQL schema to use when generating code + * @param {NetlifyGraph.NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events + * @returns + */ +const updateGraphQLOperationsFileFromPersistedDoc = (input) => { + const { logger, netlifyGraphConfig, operationsDocString, schema } = input - const hash = quickHash(operationsDocString) + writeGraphQLOperationsSourceFile({ logger, netlifyGraphConfig, operationsDocString }) + regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema }) - const relevantHasLength = 10 + const hash = quickHash(operationsDocString) - if (witnessedIncomingDocumentHashes.length > relevantHasLength) { - witnessedIncomingDocumentHashes.shift() - } + const relevantHasLength = 10 - witnessedIncomingDocumentHashes.push(hash) - } catch { - warn(`Unable to reach Netlify Graph servers in order to update Graph operations file`) + if (witnessedIncomingDocumentHashes.length > relevantHasLength) { + witnessedIncomingDocumentHashes.shift() } + + witnessedIncomingDocumentHashes.push(hash) +} + +/** + * Fetch a persisted operations doc by its id, write it to the system, and regenerate the library + * @param {object} input + * @param {string} input.siteId The site id to query against + * @param {string} input.schemaId The schema ID to query against + * @param {string} input.siteRoot Path to the root of the project + * @param {string} input.netlifyToken The (typically netlify) access token that is used for authentication, if any + * @param {string} input.docId The GraphQL operations document id to fetch + * @param {(message: string) => void=} input.logger A function that if provided will be used to log messages + * @param {GraphQL.GraphQLSchema} input.schema The GraphQL schema to use when generating code + * @param {NetlifyGraph.NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events + * @returns {Promise} + */ +const handleOperationsLibraryPersistedEvent = async (input) => { + const { schemaId, siteRoot } = input + const operationsFileContents = await fetchGraphQLOperationsLibraryFromPersistedDoc(input) + + if (!operationsFileContents) { + // `fetch` already warned + return + } + + const lockfile = NetlifyGraphLockfile.createLockfile({ operationsFileContents, schemaId }) + writeLockfile({ siteRoot, lockfile }) + updateGraphQLOperationsFileFromPersistedDoc({ ...input, operationsDocString: operationsFileContents }) } const handleCliSessionEvent = async ({ @@ -412,12 +483,14 @@ const handleCliSessionEvent = async ({ break } case 'OneGraphNetlifyCliSessionPersistedLibraryUpdatedEvent': - await updateGraphQLOperationsFileFromPersistedDoc({ + await handleOperationsLibraryPersistedEvent({ netlifyToken, docId: payload.docId, + schemaId: payload.schemaId, netlifyGraphConfig, schema, siteId, + siteRoot, }) break default: { @@ -538,6 +611,20 @@ const persistNewOperationsDocForSession = async ({ return } + const lockfile = readLockfile({ siteRoot }) + + if (!lockfile) { + warn( + `can't find a lockfile for the project while running trying to persist operations for session. To pull a remote schema (and create a lockfile), run ${chalk.yellow( + 'netlify graph:pull', + )} `, + ) + } + + // NOTE(anmonteiro): We still persist a new operations document because we + // might be checking out someone else's branch whose session we don't have + // access to. + const { branch } = gitRepoInfo() const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken }) const persistedResult = await OneGraphClient.executeCreatePersistedQueryMutation( @@ -575,6 +662,15 @@ const persistNewOperationsDocForSession = async ({ if (result.errors) { warn(`Unable to update session metadata with updated operations doc ${JSON.stringify(result.errors, null, 2)}`) + } else if (lockfile != null) { + // Now that we've persisted the document, lock it in the lockfile + const currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig) + + const newLockfile = NetlifyGraphLockfile.createLockfile({ + schemaId: lockfile.locked.schemaId, + operationsFileContents: currentOperationsDoc, + }) + writeLockfile({ siteRoot, lockfile: newLockfile }) } } @@ -611,6 +707,7 @@ const startOneGraphCLISession = async (input) => { site, state, oneGraphSessionId: input.oneGraphSessionId, + netlifyGraphConfig, }) const enabledServices = [] @@ -718,29 +815,81 @@ const generateSessionName = () => { return sessionName } +/** + * Mark a session as inactive so it doesn't show up in any UI lists, and potentially becomes available to GC later + * @param {object} input + * @param {{metadata: {schemaId:string}; id: string; appId: string; name?: string}} input.session The current session + * @param {string} input.netlifyToken The (typically netlify) access token that is used for authentication, if any + * @param {NetlifyGraphLockfile.V0_format | undefined} input.lockfile A function to call to set/get the current state of the local Netlify project + */ +const idempotentlyUpdateSessionSchemaIdFromLockfile = async (input) => { + const { lockfile, netlifyToken, session } = input + const sessionSchemaId = session.metadata && session.metadata.schemaId + const lockfileSchemaId = lockfile && lockfile.locked.schemaId + + if (lockfileSchemaId != null && sessionSchemaId !== lockfileSchemaId) { + // Local schema always wins, update the session metadata to reflect that + const siteId = session.appId + const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken }) + + log(`Found new lockfile, overwriting session ${session.name || session.id}`) + return OneGraphClient.updateCLISessionMetadata(jwt, siteId, session.id, { + ...session.metadata, + schemaId: lockfileSchemaId, + }) + } +} + /** * Ensures a cli session exists for the current checkout, or errors out if it doesn't and cannot create one. + * @param {object} input + * @param {NetlifyGraph.NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events + * @param {object} input.metadata + * @param {string} input.netlifyToken + * @param {StateConfig} input.state + * @param {string} [input.oneGraphSessionId] + * @param {any} input.site The site object */ const ensureCLISession = async (input) => { - const { metadata, netlifyToken, site, state } = input + const { metadata, netlifyGraphConfig, netlifyToken, site, state } = input let oneGraphSessionId = input.oneGraphSessionId ? input.oneGraphSessionId : loadCLISession(state) let parentCliSessionId = null const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId: site.id, nfToken: netlifyToken }) + const lockfile = readLockfile({ siteRoot: site.root }) + // Validate that session still exists and we can access it try { if (oneGraphSessionId) { - const sessionEvents = await OneGraphClient.fetchCliSessionEvents({ + const { errors, session } = await OneGraphClient.fetchCliSession({ appId: site.id, jwt, sessionId: oneGraphSessionId, + desiredEventCount: 0, }) - if (sessionEvents.errors) { - warn(`Unable to fetch cli session: ${JSON.stringify(sessionEvents.errors, null, 2)}`) + if (errors) { + warn(`Unable to fetch cli session: ${JSON.stringify(errors, null, 2)}`) log(`Creating new cli session`) parentCliSessionId = oneGraphSessionId oneGraphSessionId = null } + + // During the transition to lockfiles, write a lockfile if one isn't + // found. Later, only handling a 'OneGraphNetlifyCliSessionPersistedLibraryUpdatedEvent' + // will create or update the lockfile + // TODO(anmonteiro): remove this in the future? + if (lockfile == null && session.metadata.schemaId) { + const currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig) + log(`Generating Netlify Graph lockfile at ${NetlifyGraphLockfile.defaultLockFileName}`) + + const newLockfile = NetlifyGraphLockfile.createLockfile({ + schemaId: session.metadata.schemaId, + operationsFileContents: currentOperationsDoc, + }) + writeLockfile({ siteRoot: site.root, lockfile: newLockfile }) + } + + await idempotentlyUpdateSessionSchemaIdFromLockfile({ session, lockfile, netlifyToken }) } } catch (fetchSessionError) { warn(`Unable to fetch cli session events: ${JSON.stringify(fetchSessionError, null, 2)}`) @@ -752,17 +901,25 @@ const ensureCLISession = async (input) => { const sessionName = generateSessionName() const detectedMetadata = detectLocalCLISessionMetadata({ siteRoot: site.root }) const newSessionMetadata = parentCliSessionId ? { parentCliSessionId } : {} + const sessionMetadata = { ...detectedMetadata, ...newSessionMetadata, ...metadata, } + + if (lockfile != null) { + log(`Creating new session "${sessionName}" from lockfile`) + sessionMetadata.schemaId = lockfile.locked.schemaId + } + const oneGraphSession = await createCLISession({ netlifyToken, siteId: site.id, sessionName, metadata: sessionMetadata, }) + oneGraphSessionId = oneGraphSession.id } @@ -808,4 +965,5 @@ module.exports = { refetchAndGenerateFromOneGraph, startOneGraphCLISession, upsertMergeCLISessionMetadata, + readLockfile, } diff --git a/tests/integration/snapshots/220.command.graph.test.js.md b/tests/integration/snapshots/220.command.graph.test.js.md index de378d3a14d..9ff95823d6f 100644 --- a/tests/integration/snapshots/220.command.graph.test.js.md +++ b/tests/integration/snapshots/220.command.graph.test.js.md @@ -30,7 +30,7 @@ Generated by [AVA](https://avajs.dev). $ graph:init Initialize all the resources for Netlify Graph␊ $ graph:library Generate the Graph function library␊ $ graph:operations List all of the locally available operations␊ - $ graph:pull Pull down your local Netlify Graph schema, and process pending Graph edit events␊ + $ graph:pull Pull your remote Netlify Graph schema locally, and process pending Graph edit events␊ ` ## netlify graph completion @@ -59,5 +59,5 @@ Generated by [AVA](https://avajs.dev). $ graph:init Initialize all the resources for Netlify Graph␊ $ graph:library Generate the Graph function library␊ $ graph:operations List all of the locally available operations␊ - $ graph:pull Pull down your local Netlify Graph schema, and process pending Graph edit events␊ + $ graph:pull Pull your remote Netlify Graph schema locally, and process pending Graph edit events␊ ` diff --git a/tests/integration/snapshots/220.command.graph.test.js.snap b/tests/integration/snapshots/220.command.graph.test.js.snap index 0e8313e326d..acd1981acc5 100644 Binary files a/tests/integration/snapshots/220.command.graph.test.js.snap and b/tests/integration/snapshots/220.command.graph.test.js.snap differ