* Validate that all required configuration is present before running. * Checks for: * - Missing input variables (used before defined, no --input provided) * - Missing database integrations (SQL blocks not in integrations config) * * Throws MissingInputError or MissingIntegrationError (exit code
( file: DeepnoteFile, providedInputs: Record<string, unknown>, pythonInterpreter: string, integrations: DatabaseIntegrationConfig[], notebookName?: string )
| 764 | * Throws MissingInputError or MissingIntegrationError (exit code 2) on failure. |
| 765 | */ |
| 766 | async function validateRequirements( |
| 767 | file: DeepnoteFile, |
| 768 | providedInputs: Record<string, unknown>, |
| 769 | pythonInterpreter: string, |
| 770 | integrations: DatabaseIntegrationConfig[], |
| 771 | notebookName?: string |
| 772 | ): Promise<void> { |
| 773 | const notebooks = notebookName ? file.project.notebooks.filter(n => n.name === notebookName) : file.project.notebooks |
| 774 | |
| 775 | // Collect all blocks with their sorting keys |
| 776 | const allBlocks: Array<BlocksDeepnoteBlock & { sortingKey: string }> = [] |
| 777 | for (const notebook of notebooks) { |
| 778 | for (const block of notebook.blocks) { |
| 779 | allBlocks.push(block as BlocksDeepnoteBlock & { sortingKey: string }) |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | // === Check for missing database integrations === |
| 784 | const requiredIds = collectRequiredIntegrationIds(file, notebookName) |
| 785 | const configuredIds = new Set(integrations.map(i => i.id.toLowerCase())) |
| 786 | const missingIntegrations = requiredIds.filter(id => !configuredIds.has(id.toLowerCase())).map(id => ({ id })) |
| 787 | |
| 788 | if (missingIntegrations.length > 0) { |
| 789 | const integrationList = missingIntegrations.map(i => ` - ${i.id}`).join('\n') |
| 790 | throw new MissingIntegrationError( |
| 791 | `Missing database integration configuration.\n\n` + |
| 792 | `The following SQL blocks require database integrations that are not configured:\n` + |
| 793 | `${integrationList}\n\n` + |
| 794 | `Add the integration configuration to your integrations file.\n` + |
| 795 | `See: https://docs.deepnote.com/integrations for integration configuration.`, |
| 796 | missingIntegrations.map(i => i.id) |
| 797 | ) |
| 798 | } |
| 799 | |
| 800 | // === Check for missing inputs === |
| 801 | // Get dependency info for all blocks |
| 802 | let deps: Awaited<ReturnType<typeof getBlockDependencies>> |
| 803 | try { |
| 804 | deps = await getBlockDependencies(allBlocks, { pythonInterpreter }) |
| 805 | } catch (e) { |
| 806 | // If AST analysis fails, skip input validation (will fail at runtime instead) |
| 807 | debug(`AST analysis failed: ${e instanceof Error ? e.message : String(e)}`) |
| 808 | return |
| 809 | } |
| 810 | |
| 811 | // Build maps of block info |
| 812 | const blockDeps = new Map(deps.map(d => [d.id, d])) |
| 813 | |
| 814 | // Find input blocks and their defined variables with sort order |
| 815 | const inputVariables = new Map<string, { sortingKey: string; blockId: string }>() |
| 816 | for (const block of allBlocks) { |
| 817 | if (block.type.startsWith('input-')) { |
| 818 | const metadata = block.metadata as Record<string, unknown> |
| 819 | const varName = metadata.deepnote_variable_name as string |
| 820 | if (varName) { |
| 821 | inputVariables.set(varName, { sortingKey: block.sortingKey, blockId: block.id }) |
| 822 | } |
| 823 | } |
no test coverage detected